You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/tdeio/tdeio/tcpslavebase.cpp

1344 lines
40 KiB

/*
* $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 <tdemessagebox.h>
#ifndef Q_WS_WIN //temporary
#include <kresolver.h>
#endif
#include <tdelocale.h>
#include <dcopclient.h>
#include <tqcstring.h>
#include <tqdatastream.h>
#include <tdeapplication.h>
#include <tdeprotocolmanager.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 ); }