tcpslavebase.cpp
/*
* $Id: tcpslavebase.cpp 662342 2007-05-07 22:26:49Z dfaure $
*
* 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 <qcstring.h>
#include <qdatastream.h>
#include <kapplication.h>
#include <kprotocolmanager.h>
#include <kde_file.h>
#include "kio/tcpslavebase.h"
using namespace KIO;
class TCPSlaveBase::TcpSlaveBasePrivate
{
public:
TcpSlaveBasePrivate() : rblockSz(256), militantSSL(false), userAborted(false) {}
~TcpSlaveBasePrivate() {}
KSSL *kssl;
bool usingTLS;
KSSLCertificateCache *cc;
QString host;
QString realHost;
QString 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 QCString &protocol,
const QCString &poolSocket,
const QCString &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 QCString &protocol,
const QCString &poolSocket,
const QCString &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 QString &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"), QString::null,
"WarnOnLeaveSSLMode" );
// Move this setting into KSSL instead
KConfig *config = new KConfig("kioslaverc");
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 KSocketAddress *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
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()
{
QString 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 = QString::null;
break;
case KSSLCertificateHome::AuthPrompt:
send = false; prompt = true;
break;
default:
break;
}
}
QString ourHost;
if (!d->realHost.isEmpty()) {
ourHost = d->realHost;
} else {
ourHost = d->host;
}
// Look for a certificate on a per-host basis as an override
QString 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 = QString::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) {
QStringList certs = KSSLCertificateHome::getCertificateList();
for (QStringList::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("kio_uiserver")) {
KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
QStringList() );
}
}
QByteArray data, retval;
QCString rettype;
QDataStream arg(data, IO_WriteOnly);
arg << ourHost;
arg << certs;
arg << metaData("window-id").toInt();
bool rc = d->dcc->call("kio_uiserver", "UIServer",
"showSSLCertDialog(QString, QStringList,int)",
data, rettype, retval);
if (rc && rettype == "KSSLCertDlgRet") {
QDataStream 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
KIO::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 ? QString::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;
QString ourHost;
if (!d->realHost.isEmpty())
ourHost = d->realHost;
else ourHost = d->host;
QString theurl = QString(m_sServiceName)+"://"+ourHost+":"+QString::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()) {
QString 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",
QString::number(d->kssl->connectionInfo().getCipherUsedBits()));
setMetaData("ssl_cipher_bits",
QString::number(d->kssl->connectionInfo().getCipherBits()));
setMetaData("ssl_peer_ip", d->ip);
if (!d->realHost.isEmpty()) {
setMetaData("ssl_proxied", "true");
}
QString errorStr;
for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
it != ksvl.end(); ++it)
{
errorStr += QString::number(*it)+":";
}
setMetaData("ssl_cert_errors", errorStr);
setMetaData("ssl_peer_certificate", pc.toString());
if (pc.chain().isValid() && pc.chain().depth() > 1) {
QString theChain;
QPtrList<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", QString::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) {
QString 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 {
QString 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("kio_uiserver")) {
KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
QStringList() );
}
}
QByteArray data, ignore;
QCString ignoretype;
QDataStream arg(data, IO_WriteOnly);
arg << theurl << mOutgoingMetaData;
arg << metaData("window-id").toInt();
d->dcc->call("kio_uiserver", "UIServer",
"showSSLInfoDialog(QString,KIO::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 KDE Control Center."),
i18n("Server Authentication"));
rc = -1;
setMetaData("ssl_action", "reject");
} else {
do {
QString 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("kio_uiserver")) {
KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
QStringList() );
}
}
QByteArray data, ignore;
QCString ignoretype;
QDataStream arg(data, IO_WriteOnly);
arg << theurl << mOutgoingMetaData;
arg << metaData("window-id").toInt();
d->dcc->call("kio_uiserver", "UIServer",
"showSSLInfoDialog(QString,KIO::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
KConfig *config = new KConfig("kioslaverc");
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("kio_uiserver")) {
KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
QStringList() );
}
}
QByteArray data, ignore;
QCString ignoretype;
QDataStream arg(data, IO_WriteOnly);
arg << theurl << mOutgoingMetaData;
arg << metaData("window-id").toInt();
d->dcc->call("kio_uiserver", "UIServer",
"showSSLInfoDialog(QString,KIO::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 QString& realHost )
{
d->realHost = realHost;
}
bool TCPSlaveBase::doSSLHandShake( bool sendError )
{
kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
QString 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 ); }
KIO::TCPSlaveBase::usingTLS
bool usingTLS() const
Are we using TLS?
Definition: tcpslavebase.cpp:723
KIO::TCPSlaveBase::isConnectionValid
bool isConnectionValid()
Determines whether or not we are still connected to the remote machine.
Definition: tcpslavebase.cpp:1150
KIO::TCPSlaveBase::isSSLTunnelEnabled
bool isSSLTunnelEnabled()
Returns true if SSL tunneling is enabled.
Definition: tcpslavebase.cpp:1267
KIO::TCPSlaveBase::write
ssize_t write(const void *data, ssize_t len)
This function acts like standard write function call except it is also capable of making SSL or SOCKS...
Definition: tcpslavebase.cpp:152
KIO::AuthInfo::keepPassword
bool keepPassword
Flag to indicate the persistence of the given password.
Definition: authinfo.h:220
KIO::TCPSlaveBase::setBlockSize
void setBlockSize(int sz)
Sets the maximum size of blocks read in during calls to readLine().
Definition: tcpslavebase.cpp:183
KIO::TCPSlaveBase::readLine
ssize_t readLine(char *data, ssize_t len)
Same as above except it reads data one line at a time.
Definition: tcpslavebase.cpp:192
KIO::TCPSlaveBase::setBlockConnection
void setBlockConnection(bool b)
Sets the mode of the connection to blocking or non-blocking.
Definition: tcpslavebase.cpp:1257
KIO::SlaveBase::openPassDlg
bool openPassDlg(KIO::AuthInfo &info, const QString &errorMsg)
Prompt the user for Authorization info (login & password).
Definition: slavebase.cpp:831
KIO::TCPSlaveBase::stopTLS
void stopTLS()
Stop using TLS on the connection.
Definition: tcpslavebase.cpp:506
KIO::AuthInfo
This class is intended to make it easier to prompt for, cache and retrieve authorization information...
Definition: authinfo.h:51
KIO::SlaveBase::wasKilled
bool wasKilled() const
If your ioslave was killed by a signal, wasKilled() returns true.
Definition: slavebase.cpp:1279
KIO::TCPSlaveBase::cleanSSL
void cleanSSL()
Cleans up all SSL settings.
Definition: tcpslavebase.cpp:435
KIO::SlaveBase::config
KConfigBase * config()
Returns a configuration object to query config/meta-data information from.
Definition: slavebase.cpp:390
KIO::TCPSlaveBase::port
unsigned short int port(unsigned short int _port)
Determines the appropriate port to use.
Definition: tcpslavebase.cpp:284
KIO::SlaveBase::checkCachedAuthentication
bool checkCachedAuthentication(AuthInfo &info)
Checks for cached authentication based on parameters given by info.
Definition: slavebase.cpp:1179
KIO::MetaData
MetaData is a simple map of key/value strings.
Definition: global.h:512
KIO::SlaveBase::setMetaData
void setMetaData(const QString &key, const QString &value)
Sets meta-data to be send to the application before the first data() or finished() signal...
Definition: slavebase.cpp:359
KIO::SlaveBase::messageBox
int messageBox(MessageBoxType type, const QString &text, const QString &caption=QString::null, const QString &buttonYes=QString::null, const QString &buttonNo=QString::null)
Call this to show a message box from the slave.
Definition: slavebase.cpp:891
KIO::AuthInfo::url
KURL url
The URL for which authentication is to be stored.
Definition: authinfo.h:94
KIO::TCPSlaveBase::initializeSSL
bool initializeSSL()
Initializs all SSL variables.
Definition: tcpslavebase.cpp:424
KIO::SlaveBase
There are two classes that specifies the protocol between application (job) and kioslave.
Definition: slavebase.h:45
KIO::TCPSlaveBase::connectResult
int connectResult()
Returns the status of the connection.
Definition: tcpslavebase.cpp:1252
KIO::SlaveBase::hasMetaData
bool hasMetaData(const QString &key) const
Queries for the existence of a certain config/meta-data entry send by the application to the slave...
Definition: slavebase.cpp:373
KProtocolManager::connectTimeout
static int connectTimeout()
Returns the preferred timeout value for remote connections in seconds.
Definition: kprotocolmanager.cpp:120
KIO::SlaveBase::data
void data(const QByteArray &data)
Sends data in the slave to the job (i.e.
Definition: slavebase.cpp:413
KIO::TCPSlaveBase::startTLS
int startTLS()
Start using TLS on the connection.
Definition: tcpslavebase.cpp:451
KIO::TCPSlaveBase::closeDescriptor
void closeDescriptor()
Closes the current file descriptor.
Definition: tcpslavebase.cpp:406
KIO::SlaveBase::error
void error(int _errid, const QString &_text)
Call to signal an error.
Definition: slavebase.cpp:433
KIO::TCPSlaveBase::connectToHost
bool connectToHost(const QString &host, unsigned int port, bool sendError=true)
Performs the initial TCP connection stuff and/or SSL handshaking as necessary.
Definition: tcpslavebase.cpp:302
KIO::SlaveBase::metaData
QString metaData(const QString &key) const
Queries for config/meta-data send by the application to the slave.
Definition: slavebase.cpp:364
KIO::TCPSlaveBase::setSSLMetaData
void setSSLMetaData()
Call this if you use persistent connections and want all the metadata restored.
Definition: tcpslavebase.cpp:516
KIO::AuthInfo::password
QString password
This is required for caching.
Definition: authinfo.h:104
KIO::TCPSlaveBase::read
ssize_t read(void *data, ssize_t len)
This function acts like standard read function call except it is also capable of deciphering SSL data...
Definition: tcpslavebase.cpp:167
KIO::TCPSlaveBase::setRealHost
void setRealHost(const QString &realHost)
Sets up the the real hostname for an SSL connection that goes through a proxy server.
Definition: tcpslavebase.cpp:1277
KIO::AuthInfo::username
QString username
This is required for caching.
Definition: authinfo.h:99
KIO::TCPSlaveBase::canUseTLS
bool canUseTLS()
Can we use TLS?
Definition: tcpslavebase.cpp:524
KIO::TCPSlaveBase::atEnd
bool atEnd()
Returns true when end of data is reached.
Definition: tcpslavebase.cpp:446
KIO::TCPSlaveBase::setConnectTimeout
void setConnectTimeout(int t)
Sets how long to wait for orignally connecting to the requested before timinig out.
Definition: tcpslavebase.cpp:1262
KIO::TCPSlaveBase::setEnableSSLTunnel
void setEnableSSLTunnel(bool enable)
Set up SSL tunneling mode.
Definition: tcpslavebase.cpp:1272
KIO::AuthInfo::caption
QString caption
The text to displayed in the title bar of the password prompting dialog.
Definition: authinfo.h:126
KIO::AuthInfo::prompt
QString prompt
Information to be displayed when prompting the user for authentication information.
Definition: authinfo.h:115
KIO::TCPSlaveBase::waitForResponse
bool waitForResponse(int t)
Wait for some type of activity on the socket for the period specified by t.
Definition: tcpslavebase.cpp:1203

KDE Home | KDE Accessibility Home | Description of Access Keys