• 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     QMutableListIterator<KSslError> it(d->sslErrors);
00554     while (it.hasNext()) {
00555         // As of 4.4.0 Qt does not assign a certificate to the QSslError it emits
00556         // *in the case of HostNameMismatch*. A HostNameMismatch, however, will always
00557         // be an error of the peer certificate so we just don't check the error's
00558         // certificate().
00559 
00560         // Remove all HostNameMismatch, we have to redo name checking later.
00561         if (it.next().error() == KSslError::HostNameMismatch) {
00562             it.remove();
00563         }
00564     }
00565     // Redo name checking here and (re-)insert HostNameMismatch to sslErrors if
00566     // host name does not match any of the names in server certificate.
00567     // QSslSocket may not report HostNameMismatch error, when server
00568     // certificate was issued for the IP we are connecting to.
00569     QStringList domainPatterns(peerCert.subjectInfo(QSslCertificate::CommonName));
00570     domainPatterns += peerCert.alternateSubjectNames().values(QSsl::DnsEntry);
00571     bool names_match = false;
00572     foreach (const QString &dp, domainPatterns) {
00573         if (isMatchingHostname(dp, d->host)) {
00574             names_match = true;
00575             break;
00576         }
00577     }
00578     if (!names_match) {
00579         d->sslErrors.insert(0, KSslError(KSslError::HostNameMismatch, peerCert));
00580     }
00581 
00582     // TODO: review / rewrite / remove the comment
00583     // The app side needs the metadata now for the SSL error dialog (if any) but
00584     // the same metadata will be needed later, too. When "later" arrives the slave
00585     // may actually be connected to a different application that doesn't know
00586     // the metadata the slave sent to the previous application.
00587     // The quite important SSL indicator icon in Konqi's URL bar relies on metadata
00588     // from here, for example. And Konqi will be the second application to connect
00589     // to the slave.
00590     // Therefore we choose to have our metadata and send it, too :)
00591     d->setSslMetaData();
00592     sendAndKeepMetaData();
00593 
00594     SslResult rc = verifyServerCertificate();
00595     if (rc & ResultFailed) {
00596         d->usingSSL = false;
00597         d->clearSslMetaData();
00598         kDebug(7029) << "server certificate verification failed.";
00599         d->socket.disconnectFromHost();     //Make the connection fail (cf. ignoreSslErrors())
00600         return ResultFailed;
00601     } else if (rc & ResultOverridden) {
00602         kDebug(7029) << "server certificate verification failed but continuing at user's request.";
00603     }
00604 
00605     //"warn" when starting SSL/TLS
00606     if (metaData("ssl_activate_warnings") == "TRUE"
00607         && metaData("ssl_was_in_use") == "FALSE"
00608         && d->sslSettings.warnOnEnter()) {
00609 
00610         int msgResult = messageBox(i18n("You are about to enter secure mode. "
00611                                         "All transmissions will be encrypted "
00612                                         "unless otherwise noted.\nThis means "
00613                                         "that no third party will be able to "
00614                                         "easily observe your data in transit."),
00615                                    WarningYesNo,
00616                                    i18n("Security Information"),
00617                                    i18n("Display SSL &Information"),
00618                                    i18n("C&onnect"),
00619                                    "WarnOnEnterSSLMode");
00620         if (msgResult == KMessageBox::Yes) {
00621             messageBox(SSLMessageBox /*==the SSL info dialog*/, d->host);
00622         }
00623     }
00624 
00625     return rc;
00626 }
00627 
00628 void TCPSlaveBase::selectClientCertificate()
00629 {
00630 #if 0 //hehe
00631     QString certname;   // the cert to use this session
00632     bool send = false, prompt = false, save = false, forcePrompt = false;
00633     KSSLCertificateHome::KSSLAuthAction aa;
00634 
00635     setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed
00636 
00637     if (metaData("ssl_no_client_cert") == "TRUE") return;
00638     forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE");
00639 
00640     // Delete the old cert since we're certainly done with it now
00641     if (d->pkcs) {
00642         delete d->pkcs;
00643         d->pkcs = NULL;
00644     }
00645 
00646     if (!d->kssl) return;
00647 
00648     // Look for a general certificate
00649     if (!forcePrompt) {
00650         certname = KSSLCertificateHome::getDefaultCertificateName(&aa);
00651         switch (aa) {
00652         case KSSLCertificateHome::AuthSend:
00653             send = true; prompt = false;
00654             break;
00655         case KSSLCertificateHome::AuthDont:
00656             send = false; prompt = false;
00657             certname.clear();
00658             break;
00659         case KSSLCertificateHome::AuthPrompt:
00660             send = false; prompt = true;
00661             break;
00662         default:
00663             break;
00664         }
00665     }
00666 
00667     // Look for a certificate on a per-host basis as an override
00668     QString tmpcn = KSSLCertificateHome::getDefaultCertificateName(d->host, &aa);
00669     if (aa != KSSLCertificateHome::AuthNone) {   // we must override
00670         switch (aa) {
00671         case KSSLCertificateHome::AuthSend:
00672             send = true;
00673             prompt = false;
00674             certname = tmpcn;
00675             break;
00676         case KSSLCertificateHome::AuthDont:
00677             send = false;
00678             prompt = false;
00679             certname.clear();
00680             break;
00681         case KSSLCertificateHome::AuthPrompt:
00682             send = false;
00683             prompt = true;
00684             certname = tmpcn;
00685             break;
00686         default:
00687             break;
00688         }
00689     }
00690 
00691     // Finally, we allow the application to override anything.
00692     if (hasMetaData("ssl_demand_certificate")) {
00693         certname = metaData("ssl_demand_certificate");
00694         if (!certname.isEmpty()) {
00695             forcePrompt = false;
00696             prompt = false;
00697             send = true;
00698         }
00699     }
00700 
00701     if (certname.isEmpty() && !prompt && !forcePrompt) return;
00702 
00703     // Ok, we're supposed to prompt the user....
00704     if (prompt || forcePrompt) {
00705         QStringList certs = KSSLCertificateHome::getCertificateList();
00706 
00707         QStringList::const_iterator it = certs.begin();
00708         while (it != certs.end()) {
00709             KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it);
00710             if (pkcs && (!pkcs->getCertificate() ||
00711                          !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) {
00712                 it = certs.erase(it);
00713             } else {
00714                 ++it;
00715             }
00716             delete pkcs;
00717         }
00718 
00719         if (certs.isEmpty()) return;  // we had nothing else, and prompt failed
00720 
00721         if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kio.uiserver")) {
00722             KToolInvocation::startServiceByDesktopPath("kuiserver.desktop",
00723                     QStringList());
00724         }
00725 
00726         QDBusInterface uis("org.kde.kio.uiserver", "/UIServer", "org.kde.KIO.UIServer");
00727 
00728         QDBusMessage retVal = uis.call("showSSLCertDialog", d->host, certs, metaData("window-id").toLongLong());
00729         if (retVal.type() == QDBusMessage::ReplyMessage) {
00730             if (retVal.arguments().at(0).toBool()) {
00731                 send = retVal.arguments().at(1).toBool();
00732                 save = retVal.arguments().at(2).toBool();
00733                 certname = retVal.arguments().at(3).toString();
00734             }
00735         }
00736     }
00737 
00738     // The user may have said to not send the certificate,
00739     // but to save the choice
00740     if (!send) {
00741         if (save) {
00742             KSSLCertificateHome::setDefaultCertificate(certname, d->host,
00743                     false, false);
00744         }
00745         return;
00746     }
00747 
00748     // We're almost committed.  If we can read the cert, we'll send it now.
00749     KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname);
00750     if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) {           // We need the password
00751         KIO::AuthInfo ai;
00752         bool first = true;
00753         do {
00754             ai.prompt = i18n("Enter the certificate password:");
00755             ai.caption = i18n("SSL Certificate Password");
00756             ai.url.setProtocol("kssl");
00757             ai.url.setHost(certname);
00758             ai.username = certname;
00759             ai.keepPassword = true;
00760 
00761             bool showprompt;
00762             if (first)
00763                 showprompt = !checkCachedAuthentication(ai);
00764             else
00765                 showprompt = true;
00766             if (showprompt) {
00767                 if (!openPasswordDialog(ai, first ? QString() :
00768                                         i18n("Unable to open the certificate. Try a new password?")))
00769                     break;
00770             }
00771 
00772             first = false;
00773             pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password);
00774         } while (!pkcs);
00775 
00776     }
00777 
00778     // If we could open the certificate, let's send it
00779     if (pkcs) {
00780         if (!d->kssl->setClientCertificate(pkcs)) {
00781             messageBox(Information, i18n("The procedure to set the "
00782                                          "client certificate for the session "
00783                                          "failed."), i18n("SSL"));
00784             delete pkcs;  // we don't need this anymore
00785             pkcs = 0L;
00786         } else {
00787             kDebug(7029) << "Client SSL certificate is being used.";
00788             setMetaData("ssl_using_client_cert", "TRUE");
00789             if (save) {
00790                 KSSLCertificateHome::setDefaultCertificate(certname, d->host,
00791                         true, false);
00792             }
00793         }
00794         d->pkcs = pkcs;
00795     }
00796 #endif
00797 }
00798 
00799 TCPSlaveBase::SslResult TCPSlaveBase::verifyServerCertificate()
00800 {
00801     d->sslNoUi = hasMetaData("ssl_no_ui") && (metaData("ssl_no_ui") != "FALSE");
00802 
00803     if (d->sslErrors.isEmpty()) {
00804         return ResultOk;
00805     } else if (d->sslNoUi) {
00806         return ResultFailed;
00807     }
00808 
00809     QList<KSslError> fatalErrors = KSslCertificateManager::nonIgnorableErrors(d->sslErrors);
00810     if (!fatalErrors.isEmpty()) {
00811         //TODO message "sorry, fatal error, you can't override it"
00812         return ResultFailed;
00813     }
00814 
00815     KSslCertificateManager *const cm = KSslCertificateManager::self();
00816     KSslCertificateRule rule = cm->rule(d->socket.peerCertificateChain().first(), d->host);
00817 
00818     // remove previously seen and acknowledged errors
00819     QList<KSslError> remainingErrors = rule.filterErrors(d->sslErrors);
00820     if (remainingErrors.isEmpty()) {
00821         kDebug(7029) << "Error list empty after removing errors to be ignored. Continuing.";
00822         return ResultOk | ResultOverridden;
00823     }
00824 
00825     //### We don't ask to permanently reject the certificate
00826 
00827     QString message = i18n("The server failed the authenticity check (%1).\n\n", d->host);
00828     Q_FOREACH (const KSslError &err, d->sslErrors) {
00829         message.append(err.errorString());
00830         message.append('\n');
00831     }
00832     message = message.trimmed();
00833 
00834     int msgResult;
00835     do {
00836         msgResult = messageBox(WarningYesNoCancel, message,
00837                                i18n("Server Authentication"),
00838                                i18n("&Details"), i18n("Co&ntinue"));
00839         if (msgResult == KMessageBox::Yes) {
00840             //Details was chosen- show the certificate and error details
00841             messageBox(SSLMessageBox /*the SSL info dialog*/, d->host);
00842         } else if (msgResult == KMessageBox::Cancel) {
00843             return ResultFailed;
00844         }
00845         //fall through on KMessageBox::No
00846     } while (msgResult == KMessageBox::Yes);
00847 
00848     //Save the user's choice to ignore the SSL errors.
00849 
00850     msgResult = messageBox(WarningYesNo,
00851                             i18n("Would you like to accept this "
00852                                  "certificate forever without "
00853                                  "being prompted?"),
00854                             i18n("Server Authentication"),
00855                             i18n("&Forever"),
00856                             i18n("&Current Session only"));
00857     QDateTime ruleExpiry = QDateTime::currentDateTime();
00858     if (msgResult == KMessageBox::Yes) {
00859         //accept forever ("for a very long time")
00860         ruleExpiry = ruleExpiry.addYears(1000);
00861     } else {
00862         //accept "for a short time", half an hour.
00863         ruleExpiry = ruleExpiry.addSecs(30*60);
00864     }
00865 
00866     //TODO special cases for wildcard domain name in the certificate!
00867     //rule = KSslCertificateRule(d->socket.peerCertificateChain().first(), whatever);
00868 
00869     rule.setExpiryDateTime(ruleExpiry);
00870     rule.setIgnoredErrors(d->sslErrors);
00871     cm->setRule(rule);
00872 
00873     return ResultOk | ResultOverridden;
00874 #if 0 //### need to to do something like the old code about the main and subframe stuff
00875     kDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request");
00876     if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
00877         // Since we're the parent, we need to teach the child.
00878         setMetaData("ssl_parent_ip", d->ip);
00879         setMetaData("ssl_parent_cert", pc.toString());
00880         //  - Read from cache and see if there is a policy for this
00881         KSSLCertificateCache::KSSLCertificatePolicy cp =
00882             d->certCache->getPolicyByCertificate(pc);
00883 
00884         //  - validation code
00885         if (ksv != KSSLCertificate::Ok) {
00886             if (d->sslNoUi) {
00887                 return -1;
00888             }
00889 
00890             if (cp == KSSLCertificateCache::Unknown ||
00891                     cp == KSSLCertificateCache::Ambiguous) {
00892                 cp = KSSLCertificateCache::Prompt;
00893             } else {
00894                 // A policy was already set so let's honor that.
00895                 permacache = d->certCache->isPermanent(pc);
00896             }
00897 
00898             if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
00899                 cp = KSSLCertificateCache::Prompt;
00900 //            ksv = KSSLCertificate::Ok;
00901             }
00902 
00904 
00905         //  - cache the results
00906         d->certCache->addCertificate(pc, cp, permacache);
00907         if (doAddHost) d->certCache->addHost(pc, d->host);
00908     } else {    // Child frame
00909         //  - Read from cache and see if there is a policy for this
00910         KSSLCertificateCache::KSSLCertificatePolicy cp =
00911             d->certCache->getPolicyByCertificate(pc);
00912         isChild = true;
00913 
00914         // Check the cert and IP to make sure they're the same
00915         // as the parent frame
00916         bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
00917                                  pc.toString() == metaData("ssl_parent_cert"));
00918 
00919         if (ksv == KSSLCertificate::Ok) {
00920             if (certAndIPTheSame) {       // success
00921                 rc = 1;
00922                 setMetaData("ssl_action", "accept");
00923             } else {
00924                 /*
00925                 if (d->sslNoUi) {
00926                   return -1;
00927                 }
00928                 result = messageBox(WarningYesNo,
00929                                     i18n("The certificate is valid but does not appear to have been assigned to this server.  Do you wish to continue loading?"),
00930                                     i18n("Server Authentication"));
00931                 if (result == KMessageBox::Yes) {     // success
00932                   rc = 1;
00933                   setMetaData("ssl_action", "accept");
00934                 } else {    // fail
00935                   rc = -1;
00936                   setMetaData("ssl_action", "reject");
00937                 }
00938                 */
00939                 setMetaData("ssl_action", "accept");
00940                 rc = 1;   // Let's accept this now.  It's bad, but at least the user
00941                 // will see potential attacks in KDE3 with the pseudo-lock
00942                 // icon on the toolbar, and can investigate with the RMB
00943             }
00944         } else {
00945             if (d->sslNoUi) {
00946                 return -1;
00947             }
00948 
00949             if (cp == KSSLCertificateCache::Accept) {
00950                 if (certAndIPTheSame) {    // success
00951                     rc = 1;
00952                     setMetaData("ssl_action", "accept");
00953                 } else {   // fail
00954                     result = messageBox(WarningYesNo,
00955                                         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?"),
00956                                         i18n("Server Authentication"));
00957                     if (result == KMessageBox::Yes) {
00958                         rc = 1;
00959                         setMetaData("ssl_action", "accept");
00960                         d->certCache->addHost(pc, d->host);
00961                     } else {
00962                         rc = -1;
00963                         setMetaData("ssl_action", "reject");
00964                     }
00965                 }
00966             } else if (cp == KSSLCertificateCache::Reject) {      // fail
00967                 messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE System Settings."),
00968                            i18n("Server Authentication"));
00969                 rc = -1;
00970                 setMetaData("ssl_action", "reject");
00971             } else {
00972 
00974 
00975     return rc;
00976 #endif //#if 0
00977     return ResultOk | ResultOverridden;
00978 }
00979 
00980 
00981 bool TCPSlaveBase::isConnected() const
00982 {
00983     //QSslSocket::isValid() and therefore KTcpSocket::isValid() are shady...
00984     return d->socket.state() == KTcpSocket::ConnectedState;
00985 }
00986 
00987 
00988 bool TCPSlaveBase::waitForResponse(int t)
00989 {
00990     if (d->socket.bytesAvailable()) {
00991         return true;
00992     }
00993     return d->socket.waitForReadyRead(t * 1000);
00994 }
00995 
00996 void TCPSlaveBase::setBlocking(bool b)
00997 {
00998     if (!b) {
00999         kWarning(7029) << "Caller requested non-blocking mode, but that doesn't work";
01000         return;
01001     }
01002     d->isBlocking = b;
01003 }
01004 
01005 void TCPSlaveBase::virtual_hook(int id, void* data)
01006 {
01007     if (id == SlaveBase::AppConnectionMade) {
01008         d->sendSslMetaData();
01009     } else {
01010         SlaveBase::virtual_hook(id, data);
01011     }
01012 }

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