/*************************************************************************** * 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 #include #include #include #include #include #include "tdekrbserversocket.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; } TDEKerberosServerSocket::TDEKerberosServerSocket(TQObject *parent, const char *name) : TQSocket(parent, name), m_kerberosRequested(false), m_negotiatedMaxBufferSize(NET_SEC_BUF_SIZE) { saslData = new SASLDataPrivate; saslData->m_krbConnection = NULL; } TDEKerberosServerSocket::~TDEKerberosServerSocket() { delete saslData; } bool TDEKerberosServerSocket::open(int mode) { bool ret = TQSocket::open(mode); if (m_kerberosRequested) { initializeKerberosInterface(); } return ret; } void TDEKerberosServerSocket::close() { TQSocket::close(); } int TDEKerberosServerSocket::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 TDEKerberosServerSocket::setServiceName(TQString name) { m_serviceName = name; } void TDEKerberosServerSocket::setServerFQDN(TQString name) { m_serverFQDN = name; } TQ_LONG TDEKerberosServerSocket::readBlock(char *data, TQ_ULONG maxlen) { TQ_LONG ret; if (m_kerberosRequested) { ret = receiveEncryptedData(data, maxlen); } else { ret = TQSocket::readBlock(data, maxlen); } return ret; } TQ_LONG TDEKerberosServerSocket::writeBlock(const char *data, TQ_ULONG len) { TQ_LONG ret; if (m_kerberosRequested) { ret = transmitEncryptedData(socket(), data, len); } else { ret = TQSocket::writeBlock(data, len); } return ret; } TQ_LONG TDEKerberosServerSocket::readLine(char *data, TQ_ULONG maxlen) { TQ_LONG ret; if (m_kerberosRequested) { ret = receiveEncryptedData(data, maxlen); } else { ret = TQSocket::readLine(data, maxlen); } return ret; } TQString TDEKerberosServerSocket::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 TDEKerberosServerSocket::writeLine(TQString str) { if (m_kerberosRequested) { transmitEncryptedData(socket(), str.ascii(), str.length()); } else { TQSocket::writeBlock(str.ascii(), str.length()); } } void TDEKerberosServerSocket::freeKerberosConnection(void) { if (saslData->m_krbConnection) { sasl_dispose(&saslData->m_krbConnection); } saslData->m_krbConnection = 0; } void TDEKerberosServerSocket::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 TDEKerberosServerSocket::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); } } 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 TDEKerberosServerSocket::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 TDEKerberosServerSocket::receiveEncryptedData(char *buf, int trunclen) { unsigned int recv_len; const char *recv_data; int result; int len; char *encbuf = (char*)malloc(m_negotiatedMaxBufferSize); len = getSASLDataFromNetwork(encbuf, m_negotiatedMaxBufferSize); 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", sasl_errdetail(saslData->m_krbConnection), result); return -1; } if (recv_len > trunclen) { recv_len = trunclen; } memcpy(buf, recv_data, recv_len); } free(encbuf); return 0; } int TDEKerberosServerSocket::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 *ext_authid = NULL; unsigned int len; int count; const char *data; char user_authorized = 0; sasl_ssf_t *ssf; // FIXME // Populate these fields! char *iplocal = NULL; char *ipremote = NULL; char *localdomain = NULL; char *userdomain = NULL; 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_server_init(saslData->m_callbacks, m_serviceName.ascii()); if (result != SASL_OK) { printf("[ERROR] Initializing libsasl returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), result); return -1; } result = sasl_server_new(m_serviceName.ascii(), localdomain, userdomain, iplocal, ipremote, NULL, serverlast, &saslData->m_krbConnection); if (result != SASL_OK) { printf("[ERROR] Allocating sasl connection state returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), 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_errdetail(saslData->m_krbConnection), result); freeKerberosConnection(); return -1; } puts("[DEBUG] Generating client mechanism list..."); result = sasl_listmech(saslData->m_krbConnection, ext_authid, NULL, " ", NULL, &data, &len, &count); if (result != SASL_OK) { printf("[ERROR] Generating client mechanism list returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), result); freeKerberosConnection(); return -1; } printf("[DEBUG] Sending list of %d mechanism(s)\n\r", count); sendSASLDataToNetwork(data, len, socket()); printf("[DEBUG] Waiting for client mechanism...\n\r"); len = getSASLDataFromNetwork(buf, NET_SEC_BUF_SIZE); if (strlen(buf) < len) { printf("[DEBUG] Initial response received (%d < %d) [%s]\n\r", strlen(buf), len, buf); // An initial response is present data = buf + strlen(buf) + 1; len = len - (unsigned) strlen(buf) - 1; } else { data = NULL; len = 0; } result = sasl_server_start(saslData->m_krbConnection, buf, data, len, &data, &len); if (result != SASL_OK && result != SASL_CONTINUE) { printf("[ERROR] Starting SASL negotiation returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), result); freeKerberosConnection(); return -1; } while (result == SASL_CONTINUE) { if (data) { printf("[DEBUG] Sending response...\n\r"); sendSASLDataToNetwork(data, len, socket()); } else { printf("[ERROR] No data to send!\n\r"); freeKerberosConnection(); return -1; } printf("[DEBUG] Waiting for client reply...\n\r"); len = getSASLDataFromNetwork(buf, NET_SEC_BUF_SIZE); data = NULL; result = sasl_server_step(saslData->m_krbConnection, buf, len, &data, &len); if (result != SASL_OK && result != SASL_CONTINUE) { printf("[ERROR] Performing SASL negotiation returned %s (%d)\n\r", sasl_errdetail(saslData->m_krbConnection), result); freeKerberosConnection(); return -1; } } printf("[DEBUG] Negotiation complete\n\r"); if(serverlast && data) { printf("[DEBUG] Additional information needed to be sent\n\r"); sendSASLDataToNetwork(data, len, socket()); } 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); } return 0; }