summaryrefslogtreecommitdiffstats
path: root/tdeio/tdeio/tcpslavebase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdeio/tdeio/tcpslavebase.cpp')
-rw-r--r--tdeio/tdeio/tcpslavebase.cpp1343
1 files changed, 1343 insertions, 0 deletions
diff --git a/tdeio/tdeio/tcpslavebase.cpp b/tdeio/tdeio/tcpslavebase.cpp
new file mode 100644
index 000000000..2b7df9d7b
--- /dev/null
+++ b/tdeio/tdeio/tcpslavebase.cpp
@@ -0,0 +1,1343 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net
+ * Copyright (C) 2001-2003 George Staikos <staikos@kde.org>
+ * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
+ *
+ * This file is part of the KDE project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <time.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <ksocks.h>
+#include <kdebug.h>
+#include <ksslall.h>
+#include <ksslcertdlg.h>
+#include <kmessagebox.h>
+#ifndef Q_WS_WIN //temporary
+#include <kresolver.h>
+#endif
+
+#include <klocale.h>
+#include <dcopclient.h>
+#include <tqcstring.h>
+#include <tqdatastream.h>
+
+#include <kapplication.h>
+
+#include <kprotocolmanager.h>
+#include <kde_file.h>
+
+#include "tdeio/tcpslavebase.h"
+
+using namespace TDEIO;
+
+class TCPSlaveBase::TcpSlaveBasePrivate
+{
+public:
+
+ TcpSlaveBasePrivate() : rblockSz(256), militantSSL(false), userAborted(false) {}
+ ~TcpSlaveBasePrivate() {}
+
+ KSSL *kssl;
+ bool usingTLS;
+ KSSLCertificateCache *cc;
+ TQString host;
+ TQString realHost;
+ TQString ip;
+ DCOPClient *dcc;
+ KSSLPKCS12 *pkcs;
+
+ int status;
+ int timeout;
+ int rblockSz; // Size for reading blocks in readLine()
+ bool block;
+ bool useSSLTunneling;
+ bool needSSLHandShake;
+ bool militantSSL; // If true, we just drop a connection silently
+ // if SSL certificate check fails in any way.
+ bool userAborted;
+ MetaData savedMetaData;
+};
+
+
+TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort,
+ const TQCString &protocol,
+ const TQCString &poolSocket,
+ const TQCString &appSocket)
+ :SlaveBase (protocol, poolSocket, appSocket),
+ m_iSock(-1),
+ m_iDefaultPort(defaultPort),
+ m_sServiceName(protocol),
+ fp(0)
+{
+ // We have to have two constructors, so don't add anything
+ // else in here. Put it in doConstructorStuff() instead.
+ doConstructorStuff();
+ m_bIsSSL = false;
+}
+
+TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort,
+ const TQCString &protocol,
+ const TQCString &poolSocket,
+ const TQCString &appSocket,
+ bool useSSL)
+ :SlaveBase (protocol, poolSocket, appSocket),
+ m_iSock(-1),
+ m_bIsSSL(useSSL),
+ m_iDefaultPort(defaultPort),
+ m_sServiceName(protocol),
+ fp(0)
+{
+ doConstructorStuff();
+ if (useSSL)
+ m_bIsSSL = initializeSSL();
+}
+
+// The constructor procedures go here now.
+void TCPSlaveBase::doConstructorStuff()
+{
+ d = new TcpSlaveBasePrivate;
+ d->kssl = 0L;
+ d->ip = "";
+ d->cc = 0L;
+ d->usingTLS = false;
+ d->dcc = 0L;
+ d->pkcs = 0L;
+ d->status = -1;
+ d->timeout = KProtocolManager::connectTimeout();
+ d->block = false;
+ d->useSSLTunneling = false;
+}
+
+TCPSlaveBase::~TCPSlaveBase()
+{
+ cleanSSL();
+ if (d->usingTLS) delete d->kssl;
+ if (d->dcc) delete d->dcc;
+ if (d->pkcs) delete d->pkcs;
+ delete d;
+}
+
+ssize_t TCPSlaveBase::write(const void *data, ssize_t len)
+{
+#ifdef Q_OS_UNIX
+ if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling )
+ {
+ if ( d->needSSLHandShake )
+ (void) doSSLHandShake( true );
+ return d->kssl->write(data, len);
+ }
+ return KSocks::self()->write(m_iSock, data, len);
+#else
+ return 0;
+#endif
+}
+
+ssize_t TCPSlaveBase::read(void *data, ssize_t len)
+{
+#ifdef Q_OS_UNIX
+ if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling )
+ {
+ if ( d->needSSLHandShake )
+ (void) doSSLHandShake( true );
+ return d->kssl->read(data, len);
+ }
+ return KSocks::self()->read(m_iSock, data, len);
+#else
+ return 0;
+#endif
+}
+
+
+void TCPSlaveBase::setBlockSize(int sz)
+{
+ if (sz <= 0)
+ sz = 1;
+
+ d->rblockSz = sz;
+}
+
+
+ssize_t TCPSlaveBase::readLine(char *data, ssize_t len)
+{
+// Optimization:
+// It's small, but it probably results in a gain on very high
+// speed connections. I moved 3 if statements out of the while loop
+// so that the while loop is as small as possible. (GS)
+
+ // let's not segfault!
+ if (!data)
+ return -1;
+
+ char tmpbuf[1024]; // 1kb temporary buffer for peeking
+ *data = 0;
+ ssize_t clen = 0;
+ char *buf = data;
+ int rc = 0;
+
+if ((m_bIsSSL || d->usingTLS) && !d->useSSLTunneling) { // SSL CASE
+ if ( d->needSSLHandShake )
+ (void) doSSLHandShake( true );
+
+ while (clen < len-1) {
+ rc = d->kssl->pending();
+ if (rc > 0) { // Read a chunk
+ int bytes = rc;
+ if (bytes > d->rblockSz)
+ bytes = d->rblockSz;
+
+ rc = d->kssl->peek(tmpbuf, bytes);
+ if (rc <= 0) {
+ // FIXME: this doesn't cover rc == 0 case
+ return -1;
+ }
+
+ bytes = rc; // in case it contains no \n
+ for (int i = 0; i < rc; i++) {
+ if (tmpbuf[i] == '\n') {
+ bytes = i+1;
+ break;
+ }
+ }
+
+ if (bytes+clen >= len) // don't read too much!
+ bytes = len - clen - 1;
+
+ rc = d->kssl->read(buf, bytes);
+ if (rc > 0) {
+ clen += rc;
+ buf += (rc-1);
+ if (*buf++ == '\n')
+ break;
+ } else {
+ // FIXME: different case if rc == 0;
+ return -1;
+ }
+ } else { // Read a byte
+ rc = d->kssl->read(buf, 1);
+ if (rc <= 0) {
+ return -1;
+ // hm rc = 0 then
+ // SSL_read says to call SSL_get_error to see if
+ // this was an error. FIXME
+ } else {
+ clen++;
+ if (*buf++ == '\n')
+ break;
+ }
+ }
+ }
+} else { // NON SSL CASE
+ while (clen < len-1) {
+#ifdef Q_OS_UNIX
+ rc = KSocks::self()->read(m_iSock, buf, 1);
+#else
+ rc = 0;
+#endif
+ if (rc <= 0) {
+ // FIXME: this doesn't cover rc == 0 case
+ return -1;
+ } else {
+ clen++;
+ if (*buf++ == '\n')
+ break;
+ }
+ }
+}
+
+ // Both cases fall through to here
+ *buf = 0;
+return clen;
+}
+
+unsigned short int TCPSlaveBase::port(unsigned short int _p)
+{
+ unsigned short int p = _p;
+
+ if (_p <= 0)
+ {
+ p = m_iDefaultPort;
+ }
+
+ return p;
+}
+
+// This function is simply a wrapper to establish the connection
+// to the server. It's a bit more complicated than ::connect
+// because we first have to check to see if the user specified
+// a port, and if so use it, otherwise we check to see if there
+// is a port specified in /etc/services, and if so use that
+// otherwise as a last resort use the supplied default port.
+bool TCPSlaveBase::connectToHost( const TQString &host,
+ unsigned int _port,
+ bool sendError )
+{
+#ifdef Q_OS_UNIX
+ unsigned short int p;
+ KExtendedSocket ks;
+
+ d->userAborted = false;
+
+ // - leaving SSL - warn before we even connect
+ if (metaData("main_frame_request") == "TRUE" &&
+ metaData("ssl_activate_warnings") == "TRUE" &&
+ metaData("ssl_was_in_use") == "TRUE" &&
+ !m_bIsSSL) {
+ KSSLSettings kss;
+ if (kss.warnOnLeave()) {
+ int result = messageBox( i18n("You are about to leave secure "
+ "mode. Transmissions will no "
+ "longer be encrypted.\nThis "
+ "means that a third party could "
+ "observe your data in transit."),
+ WarningContinueCancel,
+ i18n("Security Information"),
+ i18n("C&ontinue Loading"), TQString::null,
+ "WarnOnLeaveSSLMode" );
+
+ // Move this setting into KSSL instead
+ TDEConfig *config = new TDEConfig("tdeioslaverc");
+ config->setGroup("Notification Messages");
+
+ if (!config->readBoolEntry("WarnOnLeaveSSLMode", true)) {
+ config->deleteEntry("WarnOnLeaveSSLMode");
+ config->sync();
+ kss.setWarnOnLeave(false);
+ kss.save();
+ }
+ delete config;
+
+ if ( result == KMessageBox::Cancel ) {
+ d->userAborted = true;
+ return false;
+ }
+ }
+ }
+
+ d->status = -1;
+ d->host = host;
+ d->needSSLHandShake = m_bIsSSL;
+ p = port(_port);
+ ks.setAddress(host, p);
+ if ( d->timeout > -1 )
+ ks.setTimeout( d->timeout );
+
+ if (ks.connect() < 0)
+ {
+ d->status = ks.status();
+ if ( sendError )
+ {
+ if (d->status == IO_LookupError)
+ error( ERR_UNKNOWN_HOST, host);
+ else if ( d->status != -1 )
+ error( ERR_COULD_NOT_CONNECT, host);
+ }
+ return false;
+ }
+
+ m_iSock = ks.fd();
+
+ // store the IP for later
+ const TDESocketAddress *sa = ks.peerAddress();
+ if (sa)
+ d->ip = sa->nodeName();
+ else
+ d->ip = "";
+
+ ks.release(); // KExtendedSocket no longer applicable
+
+ if ( d->block != ks.blockingMode() )
+ ks.setBlockingMode( d->block );
+
+ m_iPort=p;
+
+ if (m_bIsSSL && !d->useSSLTunneling) {
+ if ( !doSSLHandShake( sendError ) )
+ return false;
+ }
+ else
+ setMetaData("ssl_in_use", "FALSE");
+
+ // Since we want to use stdio on the socket,
+ // we must fdopen it to get a file pointer,
+ // if it fails, close everything up
+ if ((fp = KDE_fdopen(m_iSock, "w+")) == 0) {
+ closeDescriptor();
+ return false;
+ }
+
+ return true;
+#else //!Q_OS_UNIX
+ return false;
+#endif //Q_OS_UNIX
+}
+
+void TCPSlaveBase::closeDescriptor()
+{
+ stopTLS();
+ if (fp) {
+ fclose(fp);
+ fp=0;
+ m_iSock=-1;
+ if (m_bIsSSL)
+ d->kssl->close();
+ }
+ if (m_iSock != -1) {
+ close(m_iSock);
+ m_iSock=-1;
+ }
+ d->ip = "";
+ d->host = "";
+}
+
+bool TCPSlaveBase::initializeSSL()
+{
+ if (m_bIsSSL) {
+ if (KSSL::doesSSLWork()) {
+ d->kssl = new KSSL;
+ return true;
+ }
+ }
+return false;
+}
+
+void TCPSlaveBase::cleanSSL()
+{
+ delete d->cc;
+
+ if (m_bIsSSL) {
+ delete d->kssl;
+ d->kssl = 0;
+ }
+ d->militantSSL = false;
+}
+
+bool TCPSlaveBase::atEnd()
+{
+ return feof(fp);
+}
+
+int TCPSlaveBase::startTLS()
+{
+ if (d->usingTLS || d->useSSLTunneling || m_bIsSSL || !KSSL::doesSSLWork())
+ return false;
+
+ d->kssl = new KSSL(false);
+ if (!d->kssl->TLSInit()) {
+ delete d->kssl;
+ return -1;
+ }
+
+ if ( !d->realHost.isEmpty() )
+ {
+ kdDebug(7029) << "Setting real hostname: " << d->realHost << endl;
+ d->kssl->setPeerHost(d->realHost);
+ } else {
+ kdDebug(7029) << "Setting real hostname: " << d->host << endl;
+ d->kssl->setPeerHost(d->host);
+ }
+
+ if (hasMetaData("ssl_session_id")) {
+ KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
+ if (s) {
+ d->kssl->setSession(s);
+ delete s;
+ }
+ }
+ certificatePrompt();
+
+ int rc = d->kssl->connect(m_iSock);
+ if (rc < 0) {
+ delete d->kssl;
+ return -2;
+ }
+
+ setMetaData("ssl_session_id", d->kssl->session()->toString());
+
+ d->usingTLS = true;
+ setMetaData("ssl_in_use", "TRUE");
+
+ if (!d->kssl->reusingSession()) {
+ rc = verifyCertificate();
+ if (rc != 1) {
+ setMetaData("ssl_in_use", "FALSE");
+ d->usingTLS = false;
+ delete d->kssl;
+ return -3;
+ }
+ }
+
+ d->savedMetaData = mOutgoingMetaData;
+ return (d->usingTLS ? 1 : 0);
+}
+
+
+void TCPSlaveBase::stopTLS()
+{
+ if (d->usingTLS) {
+ delete d->kssl;
+ d->usingTLS = false;
+ setMetaData("ssl_in_use", "FALSE");
+ }
+}
+
+
+void TCPSlaveBase::setSSLMetaData() {
+ if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL))
+ return;
+
+ mOutgoingMetaData = d->savedMetaData;
+}
+
+
+bool TCPSlaveBase::canUseTLS()
+{
+ if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork())
+ return false;
+
+ KSSLSettings kss;
+ return kss.tlsv1();
+}
+
+
+void TCPSlaveBase::certificatePrompt()
+{
+TQString certname; // the cert to use this session
+bool send = false, prompt = false, save = false, forcePrompt = false;
+KSSLCertificateHome::KSSLAuthAction aa;
+
+ setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed
+
+ if (metaData("ssl_no_client_cert") == "TRUE") return;
+ forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE");
+
+ // Delete the old cert since we're certainly done with it now
+ if (d->pkcs) {
+ delete d->pkcs;
+ d->pkcs = NULL;
+ }
+
+ if (!d->kssl) return;
+
+ // Look for a general certificate
+ if (!forcePrompt) {
+ certname = KSSLCertificateHome::getDefaultCertificateName(&aa);
+ switch(aa) {
+ case KSSLCertificateHome::AuthSend:
+ send = true; prompt = false;
+ break;
+ case KSSLCertificateHome::AuthDont:
+ send = false; prompt = false;
+ certname = TQString::null;
+ break;
+ case KSSLCertificateHome::AuthPrompt:
+ send = false; prompt = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ TQString ourHost;
+ if (!d->realHost.isEmpty()) {
+ ourHost = d->realHost;
+ } else {
+ ourHost = d->host;
+ }
+
+ // Look for a certificate on a per-host basis as an override
+ TQString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa);
+ if (aa != KSSLCertificateHome::AuthNone) { // we must override
+ switch (aa) {
+ case KSSLCertificateHome::AuthSend:
+ send = true;
+ prompt = false;
+ certname = tmpcn;
+ break;
+ case KSSLCertificateHome::AuthDont:
+ send = false;
+ prompt = false;
+ certname = TQString::null;
+ break;
+ case KSSLCertificateHome::AuthPrompt:
+ send = false;
+ prompt = true;
+ certname = tmpcn;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Finally, we allow the application to override anything.
+ if (hasMetaData("ssl_demand_certificate")) {
+ certname = metaData("ssl_demand_certificate");
+ if (!certname.isEmpty()) {
+ forcePrompt = false;
+ prompt = false;
+ send = true;
+ }
+ }
+
+ if (certname.isEmpty() && !prompt && !forcePrompt) return;
+
+ // Ok, we're supposed to prompt the user....
+ if (prompt || forcePrompt) {
+ TQStringList certs = KSSLCertificateHome::getCertificateList();
+
+ for (TQStringList::Iterator it = certs.begin(); it != certs.end(); ++it) {
+ KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it);
+ if (pkcs && (!pkcs->getCertificate() ||
+ !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) {
+ certs.remove(*it);
+ }
+ delete pkcs;
+ }
+
+ if (certs.isEmpty()) return; // we had nothing else, and prompt failed
+
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+ }
+
+ TQByteArray data, retval;
+ TQCString rettype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << ourHost;
+ arg << certs;
+ arg << metaData("window-id").toInt();
+ bool rc = d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLCertDialog(TQString, TQStringList,int)",
+ data, rettype, retval);
+
+ if (rc && rettype == "KSSLCertDlgRet") {
+ TQDataStream retStream(retval, IO_ReadOnly);
+ KSSLCertDlgRet drc;
+ retStream >> drc;
+ if (drc.ok) {
+ send = drc.send;
+ save = drc.save;
+ certname = drc.choice;
+ }
+ }
+ }
+
+ // The user may have said to not send the certificate,
+ // but to save the choice
+ if (!send) {
+ if (save) {
+ KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
+ false, false);
+ }
+ return;
+ }
+
+ // We're almost committed. If we can read the cert, we'll send it now.
+ KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname);
+ if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) { // We need the password
+ TDEIO::AuthInfo ai;
+ bool first = true;
+ do {
+ ai.prompt = i18n("Enter the certificate password:");
+ ai.caption = i18n("SSL Certificate Password");
+ ai.url.setProtocol("kssl");
+ ai.url.setHost(certname);
+ ai.username = certname;
+ ai.keepPassword = true;
+
+ bool showprompt;
+ if (first)
+ showprompt = !checkCachedAuthentication(ai);
+ else
+ showprompt = true;
+ if (showprompt) {
+ if (!openPassDlg(ai, first ? TQString::null :
+ i18n("Unable to open the certificate. Try a new password?")))
+ break;
+ }
+
+ first = false;
+ pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password);
+ } while (!pkcs);
+
+ }
+
+ // If we could open the certificate, let's send it
+ if (pkcs) {
+ if (!d->kssl->setClientCertificate(pkcs)) {
+ messageBox(Information, i18n("The procedure to set the "
+ "client certificate for the session "
+ "failed."), i18n("SSL"));
+ delete pkcs; // we don't need this anymore
+ pkcs = 0L;
+ } else {
+ kdDebug(7029) << "Client SSL certificate is being used." << endl;
+ setMetaData("ssl_using_client_cert", "TRUE");
+ if (save) {
+ KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
+ true, false);
+ }
+ }
+ d->pkcs = pkcs;
+ }
+}
+
+
+
+bool TCPSlaveBase::usingTLS() const
+{
+ return d->usingTLS;
+}
+
+// ### remove this for KDE4 (misses const):
+bool TCPSlaveBase::usingTLS()
+{
+ return d->usingTLS;
+}
+
+
+// Returns 0 for failed verification, -1 for rejected cert and 1 for ok
+int TCPSlaveBase::verifyCertificate()
+{
+ int rc = 0;
+ bool permacache = false;
+ bool isChild = false;
+ bool _IPmatchesCN = false;
+ int result;
+ bool doAddHost = false;
+ TQString ourHost;
+
+ if (!d->realHost.isEmpty())
+ ourHost = d->realHost;
+ else ourHost = d->host;
+
+ TQString theurl = TQString(m_sServiceName)+"://"+ourHost+":"+TQString::number(m_iPort);
+
+ if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE")
+ d->militantSSL = false;
+ else if (metaData("ssl_militant") == "TRUE")
+ d->militantSSL = true;
+
+ if (!d->cc) d->cc = new KSSLCertificateCache;
+
+ KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate();
+
+ KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer);
+
+ _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
+ if (!_IPmatchesCN) {
+#ifndef Q_WS_WIN //temporary
+ KNetwork::KResolverResults res = KNetwork::KResolver::resolve(d->kssl->peerInfo().peerHost(), "80", KNetwork::KResolver::CanonName);
+ if (!res.isEmpty()) {
+ TQString old = d->kssl->peerInfo().peerHost();
+ d->kssl->peerInfo().setPeerHost(res[0].canonicalName());
+ _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
+ if (!_IPmatchesCN) {
+ d->kssl->peerInfo().setPeerHost(old);
+ }
+ }
+#endif
+ if (!_IPmatchesCN && !d->militantSSL) { // force this if the user wants it
+ if (d->cc->getHostList(pc).contains(ourHost)) {
+ _IPmatchesCN = true;
+ }
+ }
+ }
+
+ if (!_IPmatchesCN) {
+ ksvl << KSSLCertificate::InvalidHost;
+ }
+
+ KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok;
+ if (!ksvl.isEmpty())
+ ksv = ksvl.first();
+
+ /* Setting the various bits of meta-info that will be needed. */
+ setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher());
+ setMetaData("ssl_cipher_desc",
+ d->kssl->connectionInfo().getCipherDescription());
+ setMetaData("ssl_cipher_version",
+ d->kssl->connectionInfo().getCipherVersion());
+ setMetaData("ssl_cipher_used_bits",
+ TQString::number(d->kssl->connectionInfo().getCipherUsedBits()));
+ setMetaData("ssl_cipher_bits",
+ TQString::number(d->kssl->connectionInfo().getCipherBits()));
+ setMetaData("ssl_peer_ip", d->ip);
+ if (!d->realHost.isEmpty()) {
+ setMetaData("ssl_proxied", "true");
+ }
+
+ TQString errorStr;
+ for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
+ it != ksvl.end(); ++it)
+ {
+ errorStr += TQString::number(*it)+":";
+ }
+ setMetaData("ssl_cert_errors", errorStr);
+ setMetaData("ssl_peer_certificate", pc.toString());
+
+ if (pc.chain().isValid() && pc.chain().depth() > 1) {
+ TQString theChain;
+ TQPtrList<KSSLCertificate> chain = pc.chain().getChain();
+ chain.setAutoDelete(true);
+ for (KSSLCertificate *c = chain.first(); c; c = chain.next()) {
+ theChain += c->toString();
+ theChain += "\n";
+ }
+ setMetaData("ssl_peer_chain", theChain);
+ } else setMetaData("ssl_peer_chain", "");
+
+ setMetaData("ssl_cert_state", TQString::number(ksv));
+
+ if (ksv == KSSLCertificate::Ok) {
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ }
+
+ kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl;
+ if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
+ // Since we're the parent, we need to teach the child.
+ setMetaData("ssl_parent_ip", d->ip);
+ setMetaData("ssl_parent_cert", pc.toString());
+ // - Read from cache and see if there is a policy for this
+ KSSLCertificateCache::KSSLCertificatePolicy cp =
+ d->cc->getPolicyByCertificate(pc);
+
+ // - validation code
+ if (ksv != KSSLCertificate::Ok) {
+ if (d->militantSSL) {
+ return -1;
+ }
+
+ if (cp == KSSLCertificateCache::Unknown ||
+ cp == KSSLCertificateCache::Ambiguous) {
+ cp = KSSLCertificateCache::Prompt;
+ } else {
+ // A policy was already set so let's honor that.
+ permacache = d->cc->isPermanent(pc);
+ }
+
+ if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
+ cp = KSSLCertificateCache::Prompt;
+// ksv = KSSLCertificate::Ok;
+ }
+
+ // Precondition: cp is one of Reject, Accept or Prompt
+ switch (cp) {
+ case KSSLCertificateCache::Accept:
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ break;
+ case KSSLCertificateCache::Reject:
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ break;
+ case KSSLCertificateCache::Prompt:
+ {
+ do {
+ if (ksv == KSSLCertificate::InvalidHost) {
+ TQString msg = i18n("The IP address of the host %1 "
+ "does not match the one the "
+ "certificate was issued to.");
+ result = messageBox( WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&ntinue") );
+ } else {
+ TQString msg = i18n("The server certificate failed the "
+ "authenticity test (%1).");
+ result = messageBox( WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&ntinue") );
+ }
+
+ if (result == KMessageBox::Yes) {
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+
+ }
+ TQByteArray data, ignore;
+ TQCString ignoretype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << theurl << mOutgoingMetaData;
+ arg << metaData("window-id").toInt();
+ d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLInfoDialog(TQString,TDEIO::MetaData,int)",
+ data, ignoretype, ignore);
+ }
+ } while (result == KMessageBox::Yes);
+
+ if (result == KMessageBox::No) {
+ setMetaData("ssl_action", "accept");
+ rc = 1;
+ cp = KSSLCertificateCache::Accept;
+ doAddHost = true;
+ result = messageBox( WarningYesNo,
+ i18n("Would you like to accept this "
+ "certificate forever without "
+ "being prompted?"),
+ i18n("Server Authentication"),
+ i18n("&Forever"),
+ i18n("&Current Sessions Only"));
+ if (result == KMessageBox::Yes)
+ permacache = true;
+ else
+ permacache = false;
+ } else {
+ setMetaData("ssl_action", "reject");
+ rc = -1;
+ cp = KSSLCertificateCache::Prompt;
+ }
+ break;
+ }
+ default:
+ kdDebug(7029) << "TCPSlaveBase/SSL error in cert code."
+ << "Please report this to kfm-devel@kde.org."
+ << endl;
+ break;
+ }
+ }
+
+
+ // - cache the results
+ d->cc->addCertificate(pc, cp, permacache);
+ if (doAddHost) d->cc->addHost(pc, ourHost);
+ } else { // Child frame
+ // - Read from cache and see if there is a policy for this
+ KSSLCertificateCache::KSSLCertificatePolicy cp =
+ d->cc->getPolicyByCertificate(pc);
+ isChild = true;
+
+ // Check the cert and IP to make sure they're the same
+ // as the parent frame
+ bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
+ pc.toString() == metaData("ssl_parent_cert"));
+
+ if (ksv == KSSLCertificate::Ok) {
+ if (certAndIPTheSame) { // success
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ } else {
+ /*
+ if (d->militantSSL) {
+ return -1;
+ }
+ result = messageBox(WarningYesNo,
+ i18n("The certificate is valid but does not appear to have been assigned to this server. Do you wish to continue loading?"),
+ i18n("Server Authentication"));
+ if (result == KMessageBox::Yes) { // success
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ } else { // fail
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ }
+ */
+ setMetaData("ssl_action", "accept");
+ rc = 1; // Let's accept this now. It's bad, but at least the user
+ // will see potential attacks in KDE3 with the pseudo-lock
+ // icon on the toolbar, and can investigate with the RMB
+ }
+ } else {
+ if (d->militantSSL) {
+ return -1;
+ }
+
+ if (cp == KSSLCertificateCache::Accept) {
+ if (certAndIPTheSame) { // success
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ } else { // fail
+ result = messageBox(WarningYesNo,
+ i18n("You have indicated that you wish to accept this certificate, but it is not issued to the server who is presenting it. Do you wish to continue loading?"),
+ i18n("Server Authentication"));
+ if (result == KMessageBox::Yes) {
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ d->cc->addHost(pc, ourHost);
+ } else {
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ }
+ }
+ } else if (cp == KSSLCertificateCache::Reject) { // fail
+ messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the TDE Control Center."),
+ i18n("Server Authentication"));
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ } else {
+ do {
+ TQString msg = i18n("The server certificate failed the "
+ "authenticity test (%1).");
+ result = messageBox(WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&nnect"));
+ if (result == KMessageBox::Yes) {
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+ }
+ TQByteArray data, ignore;
+ TQCString ignoretype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << theurl << mOutgoingMetaData;
+ arg << metaData("window-id").toInt();
+ d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLInfoDialog(TQString,TDEIO::MetaData,int)",
+ data, ignoretype, ignore);
+ }
+ } while (result == KMessageBox::Yes);
+
+ if (result == KMessageBox::No) {
+ setMetaData("ssl_action", "accept");
+ rc = 1;
+ cp = KSSLCertificateCache::Accept;
+ result = messageBox(WarningYesNo,
+ i18n("Would you like to accept this "
+ "certificate forever without "
+ "being prompted?"),
+ i18n("Server Authentication"),
+ i18n("&Forever"),
+ i18n("&Current Sessions Only"));
+ permacache = (result == KMessageBox::Yes);
+ d->cc->addCertificate(pc, cp, permacache);
+ d->cc->addHost(pc, ourHost);
+ } else {
+ setMetaData("ssl_action", "reject");
+ rc = -1;
+ cp = KSSLCertificateCache::Prompt;
+ d->cc->addCertificate(pc, cp, permacache);
+ }
+ }
+ }
+ }
+
+
+ if (rc == -1) {
+ return rc;
+ }
+
+ if (metaData("ssl_activate_warnings") == "TRUE") {
+ // - entering SSL
+ if (!isChild && metaData("ssl_was_in_use") == "FALSE" &&
+ d->kssl->settings()->warnOnEnter()) {
+ int result;
+ do {
+ result = messageBox( i18n("You are about to "
+ "enter secure mode. "
+ "All transmissions "
+ "will be encrypted "
+ "unless otherwise "
+ "noted.\nThis means "
+ "that no third party "
+ "will be able to "
+ "easily observe your "
+ "data in transit."),
+ WarningYesNo,
+ i18n("Security Information"),
+ i18n("Display SSL "
+ "&Information"),
+ i18n("C&onnect"),
+ "WarnOnEnterSSLMode" );
+ // Move this setting into KSSL instead
+ TDEConfig *config = new TDEConfig("tdeioslaverc");
+ config->setGroup("Notification Messages");
+
+ if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) {
+ config->deleteEntry("WarnOnEnterSSLMode");
+ config->sync();
+ d->kssl->settings()->setWarnOnEnter(false);
+ d->kssl->settings()->save();
+ }
+ delete config;
+
+ if ( result == KMessageBox::Yes )
+ {
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+ }
+ TQByteArray data, ignore;
+ TQCString ignoretype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << theurl << mOutgoingMetaData;
+ arg << metaData("window-id").toInt();
+ d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLInfoDialog(TQString,TDEIO::MetaData,int)",
+ data, ignoretype, ignore);
+ }
+ } while (result != KMessageBox::No);
+ }
+
+ } // if ssl_activate_warnings
+
+
+ kdDebug(7029) << "SSL connection information follows:" << endl
+ << "+-----------------------------------------------" << endl
+ << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
+ << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
+ << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
+ << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
+ << " of " << d->kssl->connectionInfo().getCipherBits()
+ << " bits used." << endl
+ << "| PEER:" << endl
+ << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
+ << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
+ << "| Validation: " << (int)ksv << endl
+ << "| Certificate matches IP: " << _IPmatchesCN << endl
+ << "+-----------------------------------------------"
+ << endl;
+
+ // sendMetaData(); Do not call this function!!
+ return rc;
+}
+
+
+bool TCPSlaveBase::isConnectionValid()
+{
+ if ( m_iSock == -1 )
+ return false;
+
+ fd_set rdfs;
+ FD_ZERO(&rdfs);
+ FD_SET(m_iSock , &rdfs);
+
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 0;
+ int retval;
+#ifdef Q_OS_UNIX
+ do {
+ retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
+ if (wasKilled())
+ return false; // Beam us out of here
+ } while ((retval == -1) && (errno == EAGAIN));
+#else
+ retval = -1;
+#endif
+ // retval == -1 ==> Error
+ // retval == 0 ==> Connection Idle
+ // retval >= 1 ==> Connection Active
+ //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
+ // << retval << endl;
+
+ if (retval == -1)
+ return false;
+
+ if (retval == 0)
+ return true;
+
+ // Connection is active, check if it has closed.
+ char buffer[100];
+#ifdef Q_OS_UNIX
+ do {
+ retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
+
+ } while ((retval == -1) && (errno == EAGAIN));
+#else
+ retval = -1;
+#endif
+ //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
+ // << retval << endl;
+ if (retval <= 0)
+ return false; // Error or connection closed.
+
+ return true; // Connection still valid.
+}
+
+
+bool TCPSlaveBase::waitForResponse( int t )
+{
+ fd_set rd;
+ struct timeval timeout;
+
+ if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
+ if (d->kssl->pending() > 0)
+ return true;
+
+ FD_ZERO(&rd);
+ FD_SET(m_iSock, &rd);
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = t;
+ time_t startTime;
+
+ int rc;
+ int n = t;
+
+reSelect:
+ startTime = time(NULL);
+#ifdef Q_OS_UNIX
+ rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
+#else
+ rc = -1;
+#endif
+ if (wasKilled())
+ return false; // We're dead.
+
+ if (rc == -1)
+ return false;
+
+ if (FD_ISSET(m_iSock, &rd))
+ return true;
+
+ // Well it returned but it wasn't set. Let's see if it
+ // returned too early (perhaps from an errant signal) and
+ // start over with the remaining time
+ int timeDone = time(NULL) - startTime;
+ if (timeDone < n)
+ {
+ n -= timeDone;
+ timeout.tv_sec = n;
+ goto reSelect;
+ }
+
+ return false; // Timed out!
+}
+
+int TCPSlaveBase::connectResult()
+{
+ return d->status;
+}
+
+void TCPSlaveBase::setBlockConnection( bool b )
+{
+ d->block = b;
+}
+
+void TCPSlaveBase::setConnectTimeout( int t )
+{
+ d->timeout = t;
+}
+
+bool TCPSlaveBase::isSSLTunnelEnabled()
+{
+ return d->useSSLTunneling;
+}
+
+void TCPSlaveBase::setEnableSSLTunnel( bool enable )
+{
+ d->useSSLTunneling = enable;
+}
+
+void TCPSlaveBase::setRealHost( const TQString& realHost )
+{
+ d->realHost = realHost;
+}
+
+bool TCPSlaveBase::doSSLHandShake( bool sendError )
+{
+ kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
+ TQString msgHost = d->host;
+
+ d->kssl->reInitialize();
+
+ if (hasMetaData("ssl_session_id")) {
+ KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
+ if (s) {
+ d->kssl->setSession(s);
+ delete s;
+ }
+ }
+ certificatePrompt();
+
+ if ( !d->realHost.isEmpty() )
+ {
+ msgHost = d->realHost;
+ }
+
+ kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
+ d->kssl->setPeerHost(msgHost);
+
+ d->status = d->kssl->connect(m_iSock);
+ if (d->status < 0)
+ {
+ closeDescriptor();
+ if ( sendError )
+ error( ERR_COULD_NOT_CONNECT, msgHost);
+ return false;
+ }
+
+ setMetaData("ssl_session_id", d->kssl->session()->toString());
+ setMetaData("ssl_in_use", "TRUE");
+
+ if (!d->kssl->reusingSession()) {
+ int rc = verifyCertificate();
+ if ( rc != 1 ) {
+ d->status = -1;
+ closeDescriptor();
+ if ( sendError )
+ error( ERR_COULD_NOT_CONNECT, msgHost);
+ return false;
+ }
+ }
+
+ d->needSSLHandShake = false;
+
+ d->savedMetaData = mOutgoingMetaData;
+ return true;
+}
+
+
+bool TCPSlaveBase::userAborted() const
+{
+ return d->userAborted;
+}
+
+void TCPSlaveBase::virtual_hook( int id, void* data )
+{ SlaveBase::virtual_hook( id, data ); }
+