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

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