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

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
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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