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

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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