• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

tcpslavebase.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net>
00003  * Copyright (C) 2001-2003 George Staikos <staikos@kde.org>
00004  * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
00005  * Copyright (C) 2007,2008 Andreas Hartmetz <ahartmetz@gmail.com>
00006  * Copyright (C) 2008 Roland Harnau <tau@gmx.eu>
00007  * Copyright (C) 2010 Richard Moore <rich@kde.org>
00008  *
00009  * This file is part of the KDE project
00010  *
00011  * This library is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU Library General Public
00013  * License as published by the Free Software Foundation; either
00014  * version 2 of the License, or (at your option) any later version.
00015  *
00016  * This library is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Library General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Library General Public License
00022  * along with this library; see the file COPYING.LIB.  If not, write to
00023  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00024  * Boston, MA 02110-1301, USA.
00025  */
00026 
00027 #include "tcpslavebase.h"
00028 
00029 #include <config.h>
00030 
00031 #include <sys/types.h>
00032 #include <sys/uio.h>
00033 #include <sys/time.h>
00034 #include <sys/socket.h>
00035 
00036 #include <netinet/in.h>
00037 
00038 #include <time.h>
00039 #include <netdb.h>
00040 #include <unistd.h>
00041 #include <errno.h>
00042 
00043 #include <kdebug.h>
00044 #include <ksslcertificatemanager.h>
00045 #include <ksslsettings.h>
00046 #include <kmessagebox.h>
00047 #include <network/ktcpsocket.h>
00048 
00049 #include <klocale.h>
00050 #include <QtCore/QDataStream>
00051 #include <QtCore/QTime>
00052 #include <QtNetwork/QTcpSocket>
00053 #include <QtNetwork/QHostInfo>
00054 #include <QtDBus/QtDBus>
00055 
00056 #include <kapplication.h>
00057 #include <ktoolinvocation.h>
00058 #include <ksocketfactory.h>
00059 #include <kprotocolmanager.h>
00060 
00061 using namespace KIO;
00062 //using namespace KNetwork;
00063 
00064 typedef QMap<QString, QString> StringStringMap;
00065 Q_DECLARE_METATYPE(StringStringMap)
00066 
00067 namespace KIO {
00068 Q_DECLARE_OPERATORS_FOR_FLAGS(TCPSlaveBase::SslResult)
00069 }
00070 
00071 //TODO Proxy support whichever way works; KPAC reportedly does *not* work.
00072 //NOTE kded_proxyscout may or may not be interesting
00073 
00074 //TODO resurrect SSL session recycling; this means save the session on disconnect and look
00075 //for a reusable session on connect. Consider how HTTP persistent connections interact with that.
00076 
00077 //TODO in case we support SSL-lessness we need static KTcpSocket::sslAvailable() and check it
00078 //in most places we ATM check for d->isSSL.
00079 
00080 //TODO check if d->isBlocking is honored everywhere it makes sense
00081 
00082 //TODO fold KSSLSetting and KSSLCertificateHome into KSslSettings and use that everywhere.
00083 
00084 //TODO recognize partially encrypted websites as "somewhat safe"
00085 
00086 /* List of dialogs/messageboxes we need to use (current code location in parentheses)
00087  - Can the "dontAskAgainName" thing be improved?
00088 
00089  - "SSLCertDialog" [select client cert] (SlaveInterface)
00090  - Enter password for client certificate (inline)
00091  - Password for client cert was wrong. Please reenter. (inline)
00092  - Setting client cert failed. [doesn't give reason] (inline)
00093  - "SSLInfoDialog" [mostly server cert info] (SlaveInterface)
00094  - You are about to enter secure mode. Security information/Display SSL information/Connect (inline)
00095  - You are about to leave secure mode. Security information/Continue loading/Abort (inline)
00096  - Hostname mismatch: Continue/Details/Cancel (inline)
00097  - IP address mismatch: Continue/Details/Cancel (inline)
00098  - Certificate failed authenticity check: Continue/Details/Cancel (inline)
00099  - Would you like to accept this certificate forever: Yes/No/Current sessions only (inline)
00100  */
00101 
00102 
00104 class TCPSlaveBase::TcpSlaveBasePrivate
00105 {
00106 public:
00107     TcpSlaveBasePrivate(TCPSlaveBase* qq) : q(qq) {}
00108 
00109     void setSslMetaData()
00110     {
00111         sslMetaData.insert("ssl_in_use", "TRUE");
00112         KSslCipher cipher = socket.sessionCipher();
00113         sslMetaData.insert("ssl_protocol_version", socket.negotiatedSslVersionName());
00114         QString sslCipher = cipher.encryptionMethod() + '\n';
00115         sslCipher += cipher.authenticationMethod() + '\n';
00116         sslCipher += cipher.keyExchangeMethod() + '\n';
00117         sslCipher += cipher.digestMethod();
00118         sslMetaData.insert("ssl_cipher", sslCipher);
00119         sslMetaData.insert("ssl_cipher_name", cipher.name());
00120         sslMetaData.insert("ssl_cipher_used_bits", QString::number(cipher.usedBits()));
00121         sslMetaData.insert("ssl_cipher_bits", QString::number(cipher.supportedBits()));
00122         sslMetaData.insert("ssl_peer_ip", ip);
00123 
00124         // try to fill in the blanks, i.e. missing certificates, and just assume that
00125         // those belong to the peer (==website or similar) certificate.
00126         for (int i = 0; i < sslErrors.count(); i++) {
00127             if (sslErrors[i].certificate().isNull()) {
00128                 sslErrors[i] = KSslError(sslErrors[i].error(),
00129                                         socket.peerCertificateChain()[0]);
00130             }
00131         }
00132 
00133         QString errorStr;
00134         // encode the two-dimensional numeric error list using '\n' and '\t' as outer and inner separators
00135         Q_FOREACH (const QSslCertificate &cert, socket.peerCertificateChain()) {
00136             Q_FOREACH (const KSslError &error, sslErrors) {
00137                 if (error.certificate() == cert) {
00138                     errorStr += QString::number(static_cast<int>(error.error())) + '\t';
00139                 }
00140             }
00141             if (errorStr.endsWith('\t')) {
00142                 errorStr.chop(1);
00143             }
00144             errorStr += '\n';
00145         }
00146         errorStr.chop(1);
00147         sslMetaData.insert("ssl_cert_errors", errorStr);
00148 
00149         QString peerCertChain;
00150         Q_FOREACH (const QSslCertificate &cert, socket.peerCertificateChain()) {
00151             peerCertChain.append(cert.toPem());
00152             peerCertChain.append('\x01');
00153         }
00154         peerCertChain.chop(1);
00155         sslMetaData.insert("ssl_peer_chain", peerCertChain);
00156         sendSslMetaData();
00157     }
00158     
00159     void clearSslMetaData()
00160     {
00161         sslMetaData.clear();
00162         sslMetaData.insert("ssl_in_use", "FALSE");
00163         sendSslMetaData();
00164     }
00165     
00166     void sendSslMetaData()
00167     {
00168         MetaData::ConstIterator it = sslMetaData.constBegin();
00169         for (; it != sslMetaData.constEnd(); ++it) {
00170             q->setMetaData(it.key(), it.value());
00171         }
00172     }
00173 
00174     TCPSlaveBase* q;
00175 
00176     bool isBlocking;
00177 
00178     KTcpSocket socket;
00179 
00180     QString host;
00181     QString ip;
00182     quint16 port;
00183     QByteArray serviceName;
00184 
00185     KSSLSettings sslSettings;
00186     bool usingSSL;
00187     bool autoSSL;
00188     bool sslNoUi; // If true, we just drop the connection silently
00189                   // if SSL certificate check fails in some way.
00190     QList<KSslError> sslErrors;
00191     
00192     MetaData sslMetaData;
00193 };
00194 
00195 
00196 //### uh, is this a good idea??
00197 QIODevice *TCPSlaveBase::socket() const
00198 {
00199     return &d->socket;
00200 }
00201 
00202 
00203 TCPSlaveBase::TCPSlaveBase(const QByteArray &protocol,
00204                            const QByteArray &poolSocket,
00205                            const QByteArray &appSocket,
00206                            bool autoSSL)
00207  : SlaveBase(protocol, poolSocket, appSocket),
00208    d(new TcpSlaveBasePrivate(this))
00209 {
00210     d->isBlocking = true;
00211     d->port = 0;
00212     d->serviceName = protocol;
00213     d->usingSSL = false;
00214     d->autoSSL = autoSSL;
00215     d->sslNoUi = false;
00216     // Limit the read buffer size to 14 MB (14*1024*1024) (based on the upload limit
00217     // in TransferJob::slotDataReq). See the docs for QAbstractSocket::setReadBufferSize
00218     // and the BR# 187876 to understand why setting this limit is necessary.
00219     d->socket.setReadBufferSize(14680064);
00220 }
00221 
00222 
00223 TCPSlaveBase::~TCPSlaveBase()
00224 {
00225     delete d;
00226 }
00227 
00228 
00229 ssize_t TCPSlaveBase::write(const char *data, ssize_t len)
00230 {
00231     ssize_t written = d->socket.write(data, len);
00232     if (written == -1) {
00233         kDebug(7027) << "d->socket.write() returned -1! Socket error is"
00234                      << d->socket.error() << ", Socket state is" << d->socket.state();
00235     }
00236 
00237     bool success = false;
00238     if (d->isBlocking) {
00239         // Drain the tx buffer
00240         success = d->socket.waitForBytesWritten(-1);
00241     } else {
00242         // ### I don't know how to make sure that all data does get written at some point
00243         // without doing it now. There is no event loop to do it behind the scenes.
00244         // Polling in the dispatch() loop? Something timeout based?
00245         success = d->socket.waitForBytesWritten(0);
00246     }
00247 
00248     d->socket.flush();  //this is supposed to get the data on the wire faster
00249 
00250     if (d->socket.state() != KTcpSocket::ConnectedState || !success) {
00251         kDebug(7027) << "Write failed, will return -1! Socket error is"
00252                      << d->socket.error() << ", Socket state is" << d->socket.state()
00253                      << "Return value of waitForBytesWritten() is" << success;
00254         return -1;
00255     }
00256 
00257     return written;
00258 }
00259 
00260 
00261 ssize_t TCPSlaveBase::read(char* data, ssize_t len)
00262 {
00263     if (d->usingSSL && (d->socket.encryptionMode() != KTcpSocket::SslClientMode)) {
00264         d->clearSslMetaData();
00265         kDebug(7029) << "lost SSL connection.";
00266         return -1;
00267     }
00268 
00269     if (!d->socket.bytesAvailable()) {
00270         const int timeout = d->isBlocking ? -1 : readTimeout();
00271         d->socket.waitForReadyRead(timeout);
00272     }
00273 #if 0
00274     // Do not do this because its only benefit is to cause a nasty side effect
00275     // upstream in Qt. See BR# 260769.
00276     else if (d->socket.encryptionMode() != KTcpSocket::SslClientMode ||
00277                QNetworkProxy::applicationProxy().type() == QNetworkProxy::NoProxy) {
00278         // we only do this when it doesn't trigger Qt socket bugs. When it doesn't break anything
00279         // it seems to help performance.
00280         d->socket.waitForReadyRead(0);
00281     }
00282 #endif
00283     return d->socket.read(data, len);
00284 }
00285 
00286 
00287 ssize_t TCPSlaveBase::readLine(char *data, ssize_t len)
00288 {
00289     if (d->usingSSL && (d->socket.encryptionMode() != KTcpSocket::SslClientMode)) {
00290         d->clearSslMetaData();
00291         kDebug(7029) << "lost SSL connection.";
00292         return -1;
00293     }
00294 
00295     const int timeout = (d->isBlocking ? -1: readTimeout());
00296     ssize_t readTotal = 0;
00297     do {
00298         if (!d->socket.bytesAvailable())
00299             d->socket.waitForReadyRead(timeout);
00300         ssize_t readStep = d->socket.readLine(&data[readTotal], len-readTotal);
00301         if (readStep == -1 || (readStep == 0 && d->socket.state() != KTcpSocket::ConnectedState)) {
00302             return -1;
00303         }
00304         readTotal += readStep;
00305     } while (readTotal == 0 || data[readTotal-1] != '\n');
00306 
00307     return readTotal;
00308 }
00309 
00310 
00311 bool TCPSlaveBase::connectToHost(const QString &/*protocol*/,
00312                                  const QString &host,
00313                                  quint16 port)
00314 {
00315     d->clearSslMetaData(); //We have separate connection and SSL setup phases
00316 
00317     //  - leaving SSL - warn before we even connect
00318     //### see if it makes sense to move this into the HTTP ioslave which is the only
00319     //    user.
00320     if (metaData("main_frame_request") == "TRUE"  //### this looks *really* unreliable
00321           && metaData("ssl_activate_warnings") == "TRUE"
00322           && metaData("ssl_was_in_use") == "TRUE"
00323           && !d->autoSSL) {
00324         KSSLSettings kss;
00325         if (kss.warnOnLeave()) {
00326             int result = messageBox(i18n("You are about to leave secure "
00327                                          "mode. Transmissions will no "
00328                                          "longer be encrypted.\nThis "
00329                                          "means that a third party could "
00330                                          "observe your data in transit."),
00331                                     WarningContinueCancel,
00332                                     i18n("Security Information"),
00333                                     i18n("C&ontinue Loading"), QString(),
00334                                     "WarnOnLeaveSSLMode");
00335 
00336             if (result == KMessageBox::Cancel) {
00337                 error(ERR_USER_CANCELED, host);
00338                 return false;
00339             }
00340         }
00341     }
00342 
00343     KTcpSocket::SslVersion trySslVersion = KTcpSocket::TlsV1;
00344     while (true) {
00345         disconnectFromHost();  //Reset some state, even if we are already disconnected
00346         d->host = host;
00347 
00348         //FIXME! KTcpSocket doesn't know or care about protocol ports! Fix it there, then use it here.
00349 
00350         QList<QHostAddress> addresses;
00351 
00352         QHostAddress address;
00353         if (address.setAddress(host)) {
00354             addresses.append(address);
00355         } else {
00356             QHostInfo info;
00357             lookupHost(host);
00358             waitForHostInfo(info);
00359             if (info.error() != QHostInfo::NoError) {
00360                 error(ERR_UNKNOWN_HOST, host);
00361                 return false;
00362             }
00363             addresses = info.addresses();
00364         }
00365 
00366         QListIterator<QHostAddress> it(addresses);
00367         int timeout = connectTimeout() * 1000;
00368         QTime time;
00369         forever {
00370             time.start();
00371             d->socket.connectToHost(it.next(), port);
00372             if (d->socket.waitForConnected(timeout)) {
00373                 break;
00374             }
00375             timeout -= time.elapsed();
00376             if (!it.hasNext() || (timeout < 0)) {
00377                 error(ERR_COULD_NOT_CONNECT,
00378                       host + QLatin1String(": ") + d->socket.errorString());
00379                 return false;
00380             }
00381         }
00382 
00383         //### check for proxyAuthenticationRequiredError
00384 
00385         d->ip = d->socket.peerAddress().toString();
00386         d->port = d->socket.peerPort();
00387 
00388         if (d->autoSSL) {
00389             SslResult res = startTLSInternal(trySslVersion);
00390             if ((res & ResultFailed) && (res & ResultFailedEarly)
00391                 && (trySslVersion == KTcpSocket::TlsV1)) {
00392                 trySslVersion = KTcpSocket::SslV3;
00393                 continue;
00394                 //### SSL 2.0 is (close to) dead and it's a good thing, too.
00395             }
00396             if (res & ResultFailed) {
00397                 error(ERR_COULD_NOT_CONNECT,
00398                       i18nc("%1 is a host name", "%1: SSL negotiation failed", host));
00399                 return false;
00400             }
00401         }
00402         return true;
00403     }
00404     Q_ASSERT(false);
00405 }
00406 
00407 void TCPSlaveBase::disconnectFromHost()
00408 {
00409     kDebug(7027);
00410     d->host.clear();
00411     d->ip.clear();
00412     d->usingSSL = false;
00413 
00414     if (d->socket.state() == KTcpSocket::UnconnectedState) {
00415         // discard incoming data - the remote host might have disconnected us in the meantime
00416         // but the visible effect of disconnectFromHost() should stay the same.
00417         d->socket.close();
00418         return;
00419     }
00420 
00421     //### maybe save a session for reuse on SSL shutdown if and when QSslSocket
00422     //    does that. QCA::TLS can do it apparently but that is not enough if
00423     //    we want to present that as KDE API. Not a big loss in any case.
00424     d->socket.disconnectFromHost();
00425     if (d->socket.state() != KTcpSocket::UnconnectedState)
00426         d->socket.waitForDisconnected(-1); // wait for unsent data to be sent
00427     d->socket.close(); //whatever that means on a socket
00428 }
00429 
00430 bool TCPSlaveBase::isAutoSsl() const
00431 {
00432     return d->autoSSL;
00433 }
00434 
00435 bool TCPSlaveBase::isUsingSsl() const
00436 {
00437     return d->usingSSL;
00438 }
00439 
00440 quint16 TCPSlaveBase::port() const
00441 {
00442     return d->port;
00443 }
00444 
00445 bool TCPSlaveBase::atEnd() const
00446 {
00447     return d->socket.atEnd();
00448 }
00449 
00450 bool TCPSlaveBase::startSsl()
00451 {
00452     if (d->usingSSL)
00453         return false;
00454     return startTLSInternal(KTcpSocket::TlsV1) & ResultOk;
00455 }
00456 
00457 // Find out if a hostname matches an SSL certificate's Common Name (including wildcards)
00458 static bool isMatchingHostname(const QString &cnIn, const QString &hostnameIn)
00459 {
00460     const QString cn = cnIn.toLower();
00461     const QString hostname = hostnameIn.toLower();
00462 
00463     const int wildcard = cn.indexOf(QLatin1Char('*'));
00464 
00465     // Check this is a wildcard cert, if not then just compare the strings
00466     if (wildcard < 0)
00467         return cn == hostname;
00468 
00469     const int firstCnDot = cn.indexOf(QLatin1Char('.'));
00470     const int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1);
00471 
00472     // Check at least 3 components
00473     if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
00474         return false;
00475 
00476     // Check * is last character of 1st component (ie. there's a following .)
00477     if (wildcard+1 != firstCnDot)
00478         return false;
00479 
00480     // Check only one star
00481     if (cn.lastIndexOf(QLatin1Char('*')) != wildcard)
00482         return false;
00483 
00484     // Check characters preceding * (if any) match
00485     if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard)))
00486         return false;
00487 
00488     // Check characters following first . match
00489     if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot))
00490         return false;
00491 
00492     // Check if the hostname is an IP address, if so then wildcards are not allowed
00493     QHostAddress addr(hostname);
00494     if (!addr.isNull())
00495         return false;
00496 
00497     // Ok, I guess this was a wildcard CN and the hostname matches.
00498     return true;
00499 }
00500 
00501 TCPSlaveBase::SslResult TCPSlaveBase::startTLSInternal(uint v_)
00502 {
00503     KTcpSocket::SslVersion sslVersion = static_cast<KTcpSocket::SslVersion>(v_);
00504     selectClientCertificate();
00505 
00506     //setMetaData("ssl_session_id", d->kssl->session()->toString());
00507     //### we don't support session reuse for now...
00508 
00509     d->usingSSL = true;
00510 
00511     d->socket.setAdvertisedSslVersion(sslVersion);
00512 
00513     /* Usually ignoreSslErrors() would be called in the slot invoked by the sslErrors()
00514        signal but that would mess up the flow of control. We will check for errors
00515        anyway to decide if we want to continue connecting. Otherwise ignoreSslErrors()
00516        before connecting would be very insecure. */
00517     d->socket.ignoreSslErrors();
00518     d->socket.startClientEncryption();
00519     const bool encryptionStarted = d->socket.waitForEncrypted(-1);
00520 
00521     //Set metadata, among other things for the "SSL Details" dialog
00522     KSslCipher cipher = d->socket.sessionCipher();
00523 
00524     if (!encryptionStarted || d->socket.encryptionMode() != KTcpSocket::SslClientMode
00525         || cipher.isNull() || cipher.usedBits() == 0 || d->socket.peerCertificateChain().isEmpty()) {
00526         d->usingSSL = false;
00527         d->clearSslMetaData();
00528         kDebug(7029) << "Initial SSL handshake failed. encryptionStarted is"
00529                      << encryptionStarted << ", cipher.isNull() is" << cipher.isNull()
00530                      << ", cipher.usedBits() is" << cipher.usedBits()
00531                      << ", length of certificate chain is" << d->socket.peerCertificateChain().count()
00532                      << ", the socket says:" << d->socket.errorString()
00533                      << "and the list of SSL errors contains"
00534                      << d->socket.sslErrors().count() << "items.";
00535         return ResultFailed | ResultFailedEarly;
00536     }
00537 
00538     kDebug(7029) << "Cipher info - "
00539                  << " advertised SSL protocol version" << d->socket.advertisedSslVersion()
00540                  << " negotiated SSL protocol version" << d->socket.negotiatedSslVersion()
00541                  << " authenticationMethod:" << cipher.authenticationMethod()
00542                  << " encryptionMethod:" << cipher.encryptionMethod()
00543                  << " keyExchangeMethod:" << cipher.keyExchangeMethod()
00544                  << " name:" << cipher.name()
00545                  << " supportedBits:" << cipher.supportedBits()
00546                  << " usedBits:" << cipher.usedBits();
00547 
00548     // Since we connect by IP (cf. KIO::HostInfo) the SSL code will not recognize
00549     // that the site certificate belongs to the domain. We therefore do the
00550     // domain<->certificate matching here.
00551     d->sslErrors = d->socket.sslErrors();
00552     QSslCertificate peerCert = d->socket.peerCertificateChain().first();
00553     QStringList domainPatterns(peerCert.subjectInfo(QSslCertificate::CommonName));
00554     domainPatterns += peerCert.alternateSubjectNames().values(QSsl::DnsEntry);
00555     QMutableListIterator<KSslError> it(d->sslErrors);
00556     while (it.hasNext()) {
00557         // As of 4.4.0 Qt does not assign a certificate to the QSslError it emits
00558         // *in the case of HostNameMismatch*. A HostNameMismatch, however, will always
00559         // be an error of the peer certificate so we just don't check the error's
00560         // certificate().
00561         if (it.next().error() != KSslError::HostNameMismatch) {
00562             continue;
00563         }
00564         Q_FOREACH (const QString &dp, domainPatterns) {
00565             if (isMatchingHostname(dp, d->host)) {
00566                 it.remove();
00567             }
00568         }
00569     }
00570 
00571     // TODO: review / rewrite / remove the comment
00572     // The app side needs the metadata now for the SSL error dialog (if any) but
00573     // the same metadata will be needed later, too. When "later" arrives the slave
00574     // may actually be connected to a different application that doesn't know
00575     // the metadata the slave sent to the previous application.
00576     // The quite important SSL indicator icon in Konqi's URL bar relies on metadata
00577     // from here, for example. And Konqi will be the second application to connect
00578     // to the slave.
00579     // Therefore we choose to have our metadata and send it, too :)
00580     d->setSslMetaData();
00581     sendAndKeepMetaData();
00582 
00583     SslResult rc = verifyServerCertificate();
00584     if (rc & ResultFailed) {
00585         d->usingSSL = false;
00586         d->clearSslMetaData();
00587         kDebug(7029) << "server certificate verification failed.";
00588         d->socket.disconnectFromHost();     //Make the connection fail (cf. ignoreSslErrors())
00589         return ResultFailed;
00590     } else if (rc & ResultOverridden) {
00591         kDebug(7029) << "server certificate verification failed but continuing at user's request.";
00592     }
00593 
00594     //"warn" when starting SSL/TLS
00595     if (metaData("ssl_activate_warnings") == "TRUE"
00596         && metaData("ssl_was_in_use") == "FALSE"
00597         && d->sslSettings.warnOnEnter()) {
00598 
00599         int msgResult = messageBox(i18n("You are about to enter secure mode. "
00600                                         "All transmissions will be encrypted "
00601                                         "unless otherwise noted.\nThis means "
00602                                         "that no third party will be able to "
00603                                         "easily observe your data in transit."),
00604                                    WarningYesNo,
00605                                    i18n("Security Information"),
00606                                    i18n("Display SSL &Information"),
00607                                    i18n("C&onnect"),
00608                                    "WarnOnEnterSSLMode");
00609         if (msgResult == KMessageBox::Yes) {
00610             messageBox(SSLMessageBox /*==the SSL info dialog*/, d->host);
00611         }
00612     }
00613 
00614     return rc;
00615 }
00616 
00617 void TCPSlaveBase::selectClientCertificate()
00618 {
00619 #if 0 //hehe
00620     QString certname;   // the cert to use this session
00621     bool send = false, prompt = false, save = false, forcePrompt = false;
00622     KSSLCertificateHome::KSSLAuthAction aa;
00623 
00624     setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed
00625 
00626     if (metaData("ssl_no_client_cert") == "TRUE") return;
00627     forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE");
00628 
00629     // Delete the old cert since we're certainly done with it now
00630     if (d->pkcs) {
00631         delete d->pkcs;
00632         d->pkcs = NULL;
00633     }
00634 
00635     if (!d->kssl) return;
00636 
00637     // Look for a general certificate
00638     if (!forcePrompt) {
00639         certname = KSSLCertificateHome::getDefaultCertificateName(&aa);
00640         switch (aa) {
00641         case KSSLCertificateHome::AuthSend:
00642             send = true; prompt = false;
00643             break;
00644         case KSSLCertificateHome::AuthDont:
00645             send = false; prompt = false;
00646             certname.clear();
00647             break;
00648         case KSSLCertificateHome::AuthPrompt:
00649             send = false; prompt = true;
00650             break;
00651         default:
00652             break;
00653         }
00654     }
00655 
00656     // Look for a certificate on a per-host basis as an override
00657     QString tmpcn = KSSLCertificateHome::getDefaultCertificateName(d->host, &aa);
00658     if (aa != KSSLCertificateHome::AuthNone) {   // we must override
00659         switch (aa) {
00660         case KSSLCertificateHome::AuthSend:
00661             send = true;
00662             prompt = false;
00663             certname = tmpcn;
00664             break;
00665         case KSSLCertificateHome::AuthDont:
00666             send = false;
00667             prompt = false;
00668             certname.clear();
00669             break;
00670         case KSSLCertificateHome::AuthPrompt:
00671             send = false;
00672             prompt = true;
00673             certname = tmpcn;
00674             break;
00675         default:
00676             break;
00677         }
00678     }
00679 
00680     // Finally, we allow the application to override anything.
00681     if (hasMetaData("ssl_demand_certificate")) {
00682         certname = metaData("ssl_demand_certificate");
00683         if (!certname.isEmpty()) {
00684             forcePrompt = false;
00685             prompt = false;
00686             send = true;
00687         }
00688     }
00689 
00690     if (certname.isEmpty() && !prompt && !forcePrompt) return;
00691 
00692     // Ok, we're supposed to prompt the user....
00693     if (prompt || forcePrompt) {
00694         QStringList certs = KSSLCertificateHome::getCertificateList();
00695 
00696         QStringList::const_iterator it = certs.begin();
00697         while (it != certs.end()) {
00698             KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it);
00699             if (pkcs && (!pkcs->getCertificate() ||
00700                          !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) {
00701                 it = certs.erase(it);
00702             } else {
00703                 ++it;
00704             }
00705             delete pkcs;
00706         }
00707 
00708         if (certs.isEmpty()) return;  // we had nothing else, and prompt failed
00709 
00710         if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kio.uiserver")) {
00711             KToolInvocation::startServiceByDesktopPath("kuiserver.desktop",
00712                     QStringList());
00713         }
00714 
00715         QDBusInterface uis("org.kde.kio.uiserver", "/UIServer", "org.kde.KIO.UIServer");
00716 
00717         QDBusMessage retVal = uis.call("showSSLCertDialog", d->host, certs, metaData("window-id").toLongLong());
00718         if (retVal.type() == QDBusMessage::ReplyMessage) {
00719             if (retVal.arguments().at(0).toBool()) {
00720                 send = retVal.arguments().at(1).toBool();
00721                 save = retVal.arguments().at(2).toBool();
00722                 certname = retVal.arguments().at(3).toString();
00723             }
00724         }
00725     }
00726 
00727     // The user may have said to not send the certificate,
00728     // but to save the choice
00729     if (!send) {
00730         if (save) {
00731             KSSLCertificateHome::setDefaultCertificate(certname, d->host,
00732                     false, false);
00733         }
00734         return;
00735     }
00736 
00737     // We're almost committed.  If we can read the cert, we'll send it now.
00738     KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname);
00739     if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) {           // We need the password
00740         KIO::AuthInfo ai;
00741         bool first = true;
00742         do {
00743             ai.prompt = i18n("Enter the certificate password:");
00744             ai.caption = i18n("SSL Certificate Password");
00745             ai.url.setProtocol("kssl");
00746             ai.url.setHost(certname);
00747             ai.username = certname;
00748             ai.keepPassword = true;
00749 
00750             bool showprompt;
00751             if (first)
00752                 showprompt = !checkCachedAuthentication(ai);
00753             else
00754                 showprompt = true;
00755             if (showprompt) {
00756                 if (!openPasswordDialog(ai, first ? QString() :
00757                                         i18n("Unable to open the certificate. Try a new password?")))
00758                     break;
00759             }
00760 
00761             first = false;
00762             pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password);
00763         } while (!pkcs);
00764 
00765     }
00766 
00767     // If we could open the certificate, let's send it
00768     if (pkcs) {
00769         if (!d->kssl->setClientCertificate(pkcs)) {
00770             messageBox(Information, i18n("The procedure to set the "
00771                                          "client certificate for the session "
00772                                          "failed."), i18n("SSL"));
00773             delete pkcs;  // we don't need this anymore
00774             pkcs = 0L;
00775         } else {
00776             kDebug(7029) << "Client SSL certificate is being used.";
00777             setMetaData("ssl_using_client_cert", "TRUE");
00778             if (save) {
00779                 KSSLCertificateHome::setDefaultCertificate(certname, d->host,
00780                         true, false);
00781             }
00782         }
00783         d->pkcs = pkcs;
00784     }
00785 #endif
00786 }
00787 
00788 TCPSlaveBase::SslResult TCPSlaveBase::verifyServerCertificate()
00789 {
00790     d->sslNoUi = hasMetaData("ssl_no_ui") && (metaData("ssl_no_ui") != "FALSE");
00791 
00792     if (d->sslErrors.isEmpty()) {
00793         return ResultOk;
00794     } else if (d->sslNoUi) {
00795         return ResultFailed;
00796     }
00797 
00798     QList<KSslError> fatalErrors = KSslCertificateManager::nonIgnorableErrors(d->sslErrors);
00799     if (!fatalErrors.isEmpty()) {
00800         //TODO message "sorry, fatal error, you can't override it"
00801         return ResultFailed;
00802     }
00803 
00804     KSslCertificateManager *const cm = KSslCertificateManager::self();
00805     KSslCertificateRule rule = cm->rule(d->socket.peerCertificateChain().first(), d->host);
00806 
00807     // remove previously seen and acknowledged errors
00808     QList<KSslError> remainingErrors = rule.filterErrors(d->sslErrors);
00809     if (remainingErrors.isEmpty()) {
00810         kDebug(7029) << "Error list empty after removing errors to be ignored. Continuing.";
00811         return ResultOk | ResultOverridden;
00812     }
00813 
00814     //### We don't ask to permanently reject the certificate
00815 
00816     QString message = i18n("The server failed the authenticity check (%1).\n\n", d->host);
00817     Q_FOREACH (const KSslError &err, d->sslErrors) {
00818         message.append(err.errorString());
00819         message.append('\n');
00820     }
00821     message = message.trimmed();
00822 
00823     int msgResult;
00824     do {
00825         msgResult = messageBox(WarningYesNoCancel, message,
00826                                i18n("Server Authentication"),
00827                                i18n("&Details"), i18n("Co&ntinue"));
00828         if (msgResult == KMessageBox::Yes) {
00829             //Details was chosen- show the certificate and error details
00830             messageBox(SSLMessageBox /*the SSL info dialog*/, d->host);
00831         } else if (msgResult == KMessageBox::Cancel) {
00832             return ResultFailed;
00833         }
00834         //fall through on KMessageBox::No
00835     } while (msgResult == KMessageBox::Yes);
00836 
00837     //Save the user's choice to ignore the SSL errors.
00838 
00839     msgResult = messageBox(WarningYesNo,
00840                             i18n("Would you like to accept this "
00841                                  "certificate forever without "
00842                                  "being prompted?"),
00843                             i18n("Server Authentication"),
00844                             i18n("&Forever"),
00845                             i18n("&Current Session only"));
00846     QDateTime ruleExpiry = QDateTime::currentDateTime();
00847     if (msgResult == KMessageBox::Yes) {
00848         //accept forever ("for a very long time")
00849         ruleExpiry = ruleExpiry.addYears(1000);
00850     } else {
00851         //accept "for a short time", half an hour.
00852         ruleExpiry = ruleExpiry.addSecs(30*60);
00853     }
00854 
00855     //TODO special cases for wildcard domain name in the certificate!
00856     //rule = KSslCertificateRule(d->socket.peerCertificateChain().first(), whatever);
00857 
00858     rule.setExpiryDateTime(ruleExpiry);
00859     rule.setIgnoredErrors(d->sslErrors);
00860     cm->setRule(rule);
00861 
00862     return ResultOk | ResultOverridden;
00863 #if 0 //### need to to do something like the old code about the main and subframe stuff
00864     kDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request");
00865     if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
00866         // Since we're the parent, we need to teach the child.
00867         setMetaData("ssl_parent_ip", d->ip);
00868         setMetaData("ssl_parent_cert", pc.toString());
00869         //  - Read from cache and see if there is a policy for this
00870         KSSLCertificateCache::KSSLCertificatePolicy cp =
00871             d->certCache->getPolicyByCertificate(pc);
00872 
00873         //  - validation code
00874         if (ksv != KSSLCertificate::Ok) {
00875             if (d->sslNoUi) {
00876                 return -1;
00877             }
00878 
00879             if (cp == KSSLCertificateCache::Unknown ||
00880                     cp == KSSLCertificateCache::Ambiguous) {
00881                 cp = KSSLCertificateCache::Prompt;
00882             } else {
00883                 // A policy was already set so let's honor that.
00884                 permacache = d->certCache->isPermanent(pc);
00885             }
00886 
00887             if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
00888                 cp = KSSLCertificateCache::Prompt;
00889 //            ksv = KSSLCertificate::Ok;
00890             }
00891 
00893 
00894         //  - cache the results
00895         d->certCache->addCertificate(pc, cp, permacache);
00896         if (doAddHost) d->certCache->addHost(pc, d->host);
00897     } else {    // Child frame
00898         //  - Read from cache and see if there is a policy for this
00899         KSSLCertificateCache::KSSLCertificatePolicy cp =
00900             d->certCache->getPolicyByCertificate(pc);
00901         isChild = true;
00902 
00903         // Check the cert and IP to make sure they're the same
00904         // as the parent frame
00905         bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
00906                                  pc.toString() == metaData("ssl_parent_cert"));
00907 
00908         if (ksv == KSSLCertificate::Ok) {
00909             if (certAndIPTheSame) {       // success
00910                 rc = 1;
00911                 setMetaData("ssl_action", "accept");
00912             } else {
00913                 /*
00914                 if (d->sslNoUi) {
00915                   return -1;
00916                 }
00917                 result = messageBox(WarningYesNo,
00918                                     i18n("The certificate is valid but does not appear to have been assigned to this server.  Do you wish to continue loading?"),
00919                                     i18n("Server Authentication"));
00920                 if (result == KMessageBox::Yes) {     // success
00921                   rc = 1;
00922                   setMetaData("ssl_action", "accept");
00923                 } else {    // fail
00924                   rc = -1;
00925                   setMetaData("ssl_action", "reject");
00926                 }
00927                 */
00928                 setMetaData("ssl_action", "accept");
00929                 rc = 1;   // Let's accept this now.  It's bad, but at least the user
00930                 // will see potential attacks in KDE3 with the pseudo-lock
00931                 // icon on the toolbar, and can investigate with the RMB
00932             }
00933         } else {
00934             if (d->sslNoUi) {
00935                 return -1;
00936             }
00937 
00938             if (cp == KSSLCertificateCache::Accept) {
00939                 if (certAndIPTheSame) {    // success
00940                     rc = 1;
00941                     setMetaData("ssl_action", "accept");
00942                 } else {   // fail
00943                     result = messageBox(WarningYesNo,
00944                                         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?"),
00945                                         i18n("Server Authentication"));
00946                     if (result == KMessageBox::Yes) {
00947                         rc = 1;
00948                         setMetaData("ssl_action", "accept");
00949                         d->certCache->addHost(pc, d->host);
00950                     } else {
00951                         rc = -1;
00952                         setMetaData("ssl_action", "reject");
00953                     }
00954                 }
00955             } else if (cp == KSSLCertificateCache::Reject) {      // fail
00956                 messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE System Settings."),
00957                            i18n("Server Authentication"));
00958                 rc = -1;
00959                 setMetaData("ssl_action", "reject");
00960             } else {
00961 
00963 
00964     return rc;
00965 #endif //#if 0
00966     return ResultOk | ResultOverridden;
00967 }
00968 
00969 
00970 bool TCPSlaveBase::isConnected() const
00971 {
00972     //QSslSocket::isValid() and therefore KTcpSocket::isValid() are shady...
00973     return d->socket.state() == KTcpSocket::ConnectedState;
00974 }
00975 
00976 
00977 bool TCPSlaveBase::waitForResponse(int t)
00978 {
00979     if (d->socket.bytesAvailable()) {
00980         return true;
00981     }
00982     return d->socket.waitForReadyRead(t * 1000);
00983 }
00984 
00985 void TCPSlaveBase::setBlocking(bool b)
00986 {
00987     if (!b) {
00988         kWarning(7029) << "Caller requested non-blocking mode, but that doesn't work";
00989         return;
00990     }
00991     d->isBlocking = b;
00992 }
00993 
00994 void TCPSlaveBase::virtual_hook(int id, void* data)
00995 {
00996     if (id == SlaveBase::AppConnectionMade) {
00997         d->sendSslMetaData();
00998     } else {
00999         SlaveBase::virtual_hook(id, data);
01000     }
01001 }

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal