diff options
Diffstat (limited to 'lib/libtdekrb/src/tdekrbclientsocket.cpp')
-rw-r--r-- | lib/libtdekrb/src/tdekrbclientsocket.cpp | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/lib/libtdekrb/src/tdekrbclientsocket.cpp b/lib/libtdekrb/src/tdekrbclientsocket.cpp new file mode 100644 index 0000000..c9db10a --- /dev/null +++ b/lib/libtdekrb/src/tdekrbclientsocket.cpp @@ -0,0 +1,435 @@ +/*************************************************************************** + * Copyright (C) 2012 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 <stdlib.h> +#include <unistd.h> + +#include <tqapplication.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "tdekrbclientsocket.h" + +#define NET_SEC_BUF_SIZE (2048) + +class SASLDataPrivate +{ + public: + sasl_callback_t m_callbacks[N_CALLBACKS]; + sasl_conn_t *m_krbConnection; +}; + +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_negotiatedMaxBufferSize(NET_SEC_BUF_SIZE) { + saslData = new SASLDataPrivate; + saslData->m_krbConnection = NULL; +} + +TDEKerberosClientSocket::~TDEKerberosClientSocket() { + delete saslData; +} + +bool TDEKerberosClientSocket::open(int mode) { + bool ret = TQSocket::open(mode); + if (m_kerberosRequested) { + initializeKerberosInterface(); + } + return ret; +} + +void TDEKerberosClientSocket::close() { + TQSocket::close(); +} + +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)) { + ret = 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; +} + +Q_LONG TDEKerberosClientSocket::readBlock(char *data, Q_ULONG maxlen) { + Q_LONG ret; + + if (m_kerberosRequested) { + ret = receiveEncryptedData(data, maxlen); + } + else { + ret = TQSocket::readBlock(data, maxlen); + } + + return ret; +} + +Q_LONG TDEKerberosClientSocket::writeBlock(const char *data, Q_ULONG len) { + Q_LONG ret; + + if (m_kerberosRequested) { + ret = transmitEncryptedData(socket(), data, len); + } + else { + ret = TQSocket::writeBlock(data, len); + } + + return ret; +} + +Q_LONG TDEKerberosClientSocket::readLine(char *data, Q_ULONG maxlen) { + Q_LONG ret; + + if (m_kerberosRequested) { + ret = getSASLDataFromNetwork(data, maxlen); + } + else { + ret = TQSocket::readLine(data, maxlen); + } + + return ret; +} + +TQString TDEKerberosClientSocket::readLine() { + TQString ret; + char *buf; + + if (m_kerberosRequested) { + buf = (char*)malloc(m_negotiatedMaxBufferSize); + receiveEncryptedData(buf, m_negotiatedMaxBufferSize); + ret = TQString(buf); + free(buf); + } + else { + ret = TQSocket::readLine(); + } + + return ret; +} + +void TDEKerberosClientSocket::writeLine(TQString str) { + if (m_kerberosRequested) { + transmitEncryptedData(socket(), str.ascii(), str.length()); + } + else { + TQSocket::writeBlock(str.ascii(), str.length()); + } +} + +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, int netfd) { + char *buf; + unsigned len, alloclen; + int result; + + alloclen = ((length / 3) + 1) * 4 + 1; + buf = (char*)malloc(alloclen); + 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; + } + + len = strlen(buf); + buf[len] = '\n'; + buf[len+1] = 0; + write(netfd, buf, len+1); + + free(buf); +} + +unsigned int TDEKerberosClientSocket::getSASLDataFromNetwork(char *buf, int trunclen) { + unsigned int len; + int result; + + TQByteArray ba(2048); + + len = 0; + while (1) { + tqApp->processEvents(); + if (state() != TQSocket::Connected) { + return -1; + } + if (TQSocket::readBlock(ba.data()+len, 1) > 0) { + if (ba.data()[len] == '\n') { + ba.data()[len] = 0; + break; + } + if (ba.data()[len] != '\r') { + len++; + } + } + if (len >= (ba.size()-1)) { + ba.resize(ba.size()+2048); + break; + } + } + + len = strlen(ba.data()); + result = sasl_decode64(ba.data(), strlen(ba.data()), buf, trunclen, &len); + if (result != SASL_OK) { + printf("[ERROR] Decoding data from base64 returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); + return -1; + } + buf[len] = '\0'; + + return len; +} + +int TDEKerberosClientSocket::transmitEncryptedData(int fd, const char* readbuf, int cc) { + int result = 0; + unsigned int len; + const char *data; + + result=sasl_encode(saslData->m_krbConnection, readbuf, cc, &data, &len); + if (result != SASL_OK) { + printf("[ERROR] Encrypting data returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), result); + return -1; + } + sendSASLDataToNetwork(data, len, fd); + + return 0; +} + +int TDEKerberosClientSocket::receiveEncryptedData(char *buf, int trunclen) { + unsigned int recv_len; + const char *recv_data; + int result; + int len; + + len = getSASLDataFromNetwork(buf, trunclen); + if (len >= 0) { + result=sasl_decode(saslData->m_krbConnection, buf, len, &recv_data, &recv_len); + if (result != SASL_OK) { + printf("[ERROR] Decrypting data returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), result); + return -1; + } + strncpy(buf, recv_data, trunclen); + } + + return 0; +} + +int TDEKerberosClientSocket::initializeKerberosInterface() { + if (state() != TQSocket::Connected) { + saslData->m_krbConnection = false; + return -1; + } + + sasl_callback_t *callback; + char buf[NET_SEC_BUF_SIZE]; + int result = 0; + int serverlast = 0; + sasl_security_properties_t secprops; + const char *chosenmech; + unsigned int len; + const char *data; + char user_authorized = 0; + sasl_ssf_t *ssf; + char *iplocal = NULL; + char *ipremote = NULL; + const char *service = m_serviceName.ascii(); + const char *fqdn = m_serverFQDN.ascii(); + + callback = saslData->m_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; + + // Initialize default data structures + memset(&secprops, 0L, sizeof(secprops)); + secprops.maxbufsize = NET_SEC_BUF_SIZE; + secprops.max_ssf = UINT_MAX; + + result = sasl_client_init(saslData->m_callbacks); + if (result != SASL_OK) { + printf("[ERROR] Initializing libsasl returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); + return -1; + } + + result = sasl_client_new(service, fqdn, iplocal, ipremote, NULL, serverlast, &saslData->m_krbConnection); + if (result != SASL_OK) { + printf("[ERROR] Allocating sasl connection state returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); + return -1; + } + + result = sasl_setprop(saslData->m_krbConnection, SASL_SEC_PROPS, &secprops); + if (result != SASL_OK) { + printf("[ERROR] Setting security properties returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); + freeKerberosConnection(); + return -1; + } + + printf("[DEBUG] Waiting for mechanism list from server...\n\r"); + len = getSASLDataFromNetwork(buf, NET_SEC_BUF_SIZE); + + printf("Choosing best mechanism from: %s\n", buf); + + result = sasl_client_start(saslData->m_krbConnection, buf, NULL, &data, &len, &chosenmech); + if (result != SASL_OK && result != SASL_CONTINUE) { + printf("[ERROR] Starting SASL negotiation returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); + freeKerberosConnection(); + return -1; + } + + 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(); + return -1; + } + 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, socket()); + + while (result == SASL_CONTINUE) { + printf("[DEBUG] Waiting for server reply...\n\r"); + len = getSASLDataFromNetwork(buf, NET_SEC_BUF_SIZE); + if (state() != TQSocket::Connected) { + return -1; + } + result = sasl_client_step(saslData->m_krbConnection, buf, len, NULL, &data, &len); + if (result != SASL_OK && result != SASL_CONTINUE) { + printf("[ERROR] Performing SASL negotiation returned %s (%d)\n\r", sasl_errstring(result, NULL, NULL), result); + freeKerberosConnection(); + return -1; + } + if (data && len) { + printf("[DEBUG] Sending response...\n\r"); + sendSASLDataToNetwork(data, len, socket()); + } + else if (result != SASL_OK || !serverlast) { + sendSASLDataToNetwork("", 0, socket()); + } + } + printf("[DEBUG] Negotiation complete!\n\r"); + + result = sasl_getprop(saslData->m_krbConnection, SASL_USERNAME, (const void **)&data); + if (result != SASL_OK) { + printf("[WARNING] Unable to determine authenticated username!\n\r"); + } + else { + printf("[DEBUG] Authenticated username: %s\n\r", data ? data : "(NULL)"); + } + + result = sasl_getprop(saslData->m_krbConnection, SASL_DEFUSERREALM, (const void **)&data); + if (result != SASL_OK) { + printf("[WARNING] Unable to determine authenticated realm!\n\r"); + } + else { + printf("[DEBUG] Authenticated realm: %s\n\r", data ? data : "(NULL)"); + } + + result = sasl_getprop(saslData->m_krbConnection, SASL_SSF, (const void **)&ssf); + if (result != SASL_OK) { + printf("[WARNING] Unable to determine SSF!\n\r"); + } + else { + printf("[DEBUG] Authenticated SSF: %d\n", *ssf); + } + + result = sasl_getprop(saslData->m_krbConnection, SASL_MAXOUTBUF, (const void **)&m_negotiatedMaxBufferSize); + if (result != SASL_OK) { + printf("[WARNING] Unable to determine maximum buffer size!\n\r"); + m_negotiatedMaxBufferSize = NET_SEC_BUF_SIZE; + } + else { + printf("[DEBUG] Maximum buffer size: %d\n", m_negotiatedMaxBufferSize); + } + + return 0; +}
\ No newline at end of file |