kio Library: tcpslavebase.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# tcpslavebase.cpp

    00001 /*
    00002  * $Id: tcpslavebase.cpp 415455 2005-05-18 16:34:56Z deller $
    00003  *
    00004  * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net
    00005  * Copyright (C) 2001-2003 George Staikos <staikos@kde.org>
    00006  * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
    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., 59 Temple Place - Suite 330,
    00023  * Boston, MA 02111-1307, USA.
    00024  */
    00025
    00026 #ifdef HAVE_CONFIG_H
    00027 #include <config.h>
    00028 #endif
    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 <ksocks.h>
    00043 #include <kdebug.h>
    00044 #include <ksslall.h>
    00045 #include <ksslcertdlg.h>
    00046 #include <kmessagebox.h>
    00047
    00048 #include <klocale.h>
    00049 #include <dcopclient.h>
    00050 #include <qcstring.h>
    00051 #include <qdatastream.h>
    00052
    00053 #include <kapplication.h>
    00054
    00055 #include <kprotocolmanager.h>
    00056 #include <kde_file.h>
    00057
    00058 #include "kio/tcpslavebase.h"
    00059
    00060 using namespace KIO;
    00061
    00062 class TCPSlaveBase::TcpSlaveBasePrivate
    00063 {
    00064 public:
    00065
    00066   TcpSlaveBasePrivate() : rblockSz(256), militantSSL(false), userAborted(false) {}
    00067   ~TcpSlaveBasePrivate() {}
    00068
    00069   KSSL *kssl;
    00070   bool usingTLS;
    00071   KSSLCertificateCache *cc;
    00072   QString host;
    00073   QString realHost;
    00074   QString ip;
    00075   DCOPClient *dcc;
    00076   KSSLPKCS12 *pkcs;
    00077
    00078   int status;
    00079   int timeout;
    00080   int rblockSz;      // Size for reading blocks in readLine()
    00081   bool block;
    00082   bool useSSLTunneling;
    00083   bool needSSLHandShake;
    00084   bool militantSSL;              // If true, we just drop a connection silently
    00085                                  // if SSL certificate check fails in any way.
    00086   bool userAborted;
    00087   MetaData savedMetaData;
    00088 };
    00089
    00090
    00091 TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort,
    00092                            const QCString &protocol,
    00093                            const QCString &poolSocket,
    00094                            const QCString &appSocket)
    00095              :SlaveBase (protocol, poolSocket, appSocket),
    00096               m_iSock(-1),
    00097               m_iDefaultPort(defaultPort),
    00098               m_sServiceName(protocol),
    00099               fp(0)
    00100 {
    00101     // We have to have two constructors, so don't add anything
    00102     // else in here. Put it in doConstructorStuff() instead.
    00103     doConstructorStuff();
    00104     m_bIsSSL = false;
    00105 }
    00106
    00107 TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort,
    00108                            const QCString &protocol,
    00109                            const QCString &poolSocket,
    00110                            const QCString &appSocket,
    00111                            bool useSSL)
    00112              :SlaveBase (protocol, poolSocket, appSocket),
    00113               m_iSock(-1),
    00114               m_bIsSSL(useSSL),
    00115               m_iDefaultPort(defaultPort),
    00116               m_sServiceName(protocol),
    00117               fp(0)
    00118 {
    00119     doConstructorStuff();
    00120     if (useSSL)
    00121         m_bIsSSL = initializeSSL();
    00122 }
    00123
    00124 // The constructor procedures go here now.
    00125 void TCPSlaveBase::doConstructorStuff()
    00126 {
    00127     d = new TcpSlaveBasePrivate;
    00128     d->kssl = 0L;
    00129     d->ip = "";
    00130     d->cc = 0L;
    00131     d->usingTLS = false;
    00132     d->dcc = 0L;
    00133     d->pkcs = 0L;
    00134     d->status = -1;
    00135     d->timeout = KProtocolManager::connectTimeout();
    00136     d->block = false;
    00137     d->useSSLTunneling = false;
    00138 }
    00139
    00140 TCPSlaveBase::~TCPSlaveBase()
    00141 {
    00142     cleanSSL();
    00143     if (d->usingTLS) delete d->kssl;
    00144     if (d->dcc) delete d->dcc;
    00145     if (d->pkcs) delete d->pkcs;
    00146     delete d;
    00147 }
    00148
    00149 ssize_t TCPSlaveBase::write(const void *data, ssize_t len)
    00150 {
    00151 #ifdef Q_OS_UNIX
    00152     if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling )
    00153     {
    00154         if ( d->needSSLHandShake )
    00155             (void) doSSLHandShake( true );
    00156         return d->kssl->write(data, len);
    00157     }
    00158     return KSocks::self()->write(m_iSock, data, len);
    00159 #else
    00160     return 0;
    00161 #endif
    00162 }
    00163
    00164 ssize_t TCPSlaveBase::read(void *data, ssize_t len)
    00165 {
    00166 #ifdef Q_OS_UNIX
    00167     if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling )
    00168     {
    00169         if ( d->needSSLHandShake )
    00170             (void) doSSLHandShake( true );
    00171         return d->kssl->read(data, len);
    00172     }
    00173     return KSocks::self()->read(m_iSock, data, len);
    00174 #else
    00175     return 0;
    00176 #endif
    00177 }
    00178
    00179
    00180 void TCPSlaveBase::setBlockSize(int sz)
    00181 {
    00182   if (sz <= 0)
    00183     sz = 1;
    00184
    00185   d->rblockSz = sz;
    00186 }
    00187
    00188
    00189 ssize_t TCPSlaveBase::readLine(char *data, ssize_t len)
    00190 {
    00191 // Optimization:
    00192 //           It's small, but it probably results in a gain on very high
    00193 //   speed connections.  I moved 3 if statements out of the while loop
    00194 //   so that the while loop is as small as possible.  (GS)
    00195
    00196   // let's not segfault!
    00197   if (!data)
    00198     return -1;
    00199
    00200   char tmpbuf[1024];   // 1kb temporary buffer for peeking
    00201   *data = 0;
    00202   ssize_t clen = 0;
    00203   char *buf = data;
    00204   int rc = 0;
    00205
    00206 if ((m_bIsSSL || d->usingTLS) && !d->useSSLTunneling) {       // SSL CASE
    00207   if ( d->needSSLHandShake )
    00208     (void) doSSLHandShake( true );
    00209
    00210   while (clen < len-1) {
    00211     rc = d->kssl->pending();
    00212     if (rc > 0) {   // Read a chunk
    00213       int bytes = rc;
    00214       if (bytes > d->rblockSz)
    00215          bytes = d->rblockSz;
    00216
    00217       rc = d->kssl->peek(tmpbuf, bytes);
    00218       if (rc <= 0) {
    00219         // FIXME: this doesn't cover rc == 0 case
    00220         return -1;
    00221       }
    00222
    00223       bytes = rc;   // in case it contains no \n
    00224       for (int i = 0; i < rc; i++) {
    00225         if (tmpbuf[i] == '\n') {
    00226           bytes = i+1;
    00227           break;
    00228         }
    00229       }
    00230
    00231       if (bytes+clen >= len)   // don't read too much!
    00232         bytes = len - clen - 1;
    00233
    00234       rc = d->kssl->read(buf, bytes);
    00235       if (rc > 0) {
    00236         clen += rc;
    00237         buf += (rc-1);
    00238         if (*buf++ == '\n')
    00239           break;
    00240       } else {
    00241         // FIXME: different case if rc == 0;
    00242         return -1;
    00243       }
    00244     } else {        // Read a byte
    00245       rc = d->kssl->read(buf, 1);
    00246       if (rc <= 0) {
    00247         return -1;
    00248         // hm rc = 0 then
    00249         // SSL_read says to call SSL_get_error to see if
    00250         // this was an error.    FIXME
    00251       } else {
    00252         clen++;
    00253         if (*buf++ == '\n')
    00254           break;
    00255       }
    00256     }
    00257   }
    00258 } else {                                                      // NON SSL CASE
    00259   while (clen < len-1) {
    00260 #ifdef Q_OS_UNIX
    00261     rc = KSocks::self()->read(m_iSock, buf, 1);
    00262 #else
    00263     rc = 0;
    00264 #endif
    00265     if (rc <= 0) {
    00266       // FIXME: this doesn't cover rc == 0 case
    00267       return -1;
    00268     } else {
    00269       clen++;
    00270       if (*buf++ == '\n')
    00271         break;
    00272     }
    00273   }
    00274 }
    00275
    00276   // Both cases fall through to here
    00277   *buf = 0;
    00278 return clen;
    00279 }
    00280
    00281 unsigned short int TCPSlaveBase::port(unsigned short int _p)
    00282 {
    00283     unsigned short int p = _p;
    00284
    00285     if (_p <= 0)
    00286     {
    00287         p = m_iDefaultPort;
    00288     }
    00289
    00290     return p;
    00291 }
    00292
    00293 // This function is simply a wrapper to establish the connection
    00294 // to the server.  It's a bit more complicated than ::connect
    00295 // because we first have to check to see if the user specified
    00296 // a port, and if so use it, otherwise we check to see if there
    00297 // is a port specified in /etc/services, and if so use that
    00298 // otherwise as a last resort use the supplied default port.
    00299 bool TCPSlaveBase::connectToHost( const QString &host,
    00300                                   unsigned int _port,
    00301                                   bool sendError )
    00302 {
    00303 #ifdef Q_OS_UNIX
    00304     unsigned short int p;
    00305     KExtendedSocket ks;
    00306
    00307     d->userAborted = false;
    00308
    00309     //  - leaving SSL - warn before we even connect
    00310     if (metaData("main_frame_request") == "TRUE" &&
    00311         metaData("ssl_activate_warnings") == "TRUE" &&
    00312                metaData("ssl_was_in_use") == "TRUE" &&
    00313         !m_bIsSSL) {
    00314        KSSLSettings kss;
    00315        if (kss.warnOnLeave()) {
    00316           int result = messageBox( i18n("You are about to leave secure "
    00317                                         "mode. Transmissions will no "
    00318                                         "longer be encrypted.\nThis "
    00319                                         "means that a third party could "
    00320                                         "observe your data in transit."),
    00321                                    WarningContinueCancel,
    00322                                    i18n("Security Information"),
    00323                                    i18n("C&ontinue Loading"), QString::null,
    00324                                    "WarnOnLeaveSSLMode" );
    00325
    00326            // Move this setting into KSSL instead
    00327           KConfig *config = new KConfig("kioslaverc");
    00328           config->setGroup("Notification Messages");
    00329
    00330           if (!config->readBoolEntry("WarnOnLeaveSSLMode", true)) {
    00331               config->deleteEntry("WarnOnLeaveSSLMode");
    00332               config->sync();
    00333               kss.setWarnOnLeave(false);
    00334               kss.save();
    00335           }
    00336           delete config;
    00337
    00338           if ( result == KMessageBox::Cancel ) {
    00339              d->userAborted = true;
    00340              return false;
    00341           }
    00342        }
    00343     }
    00344
    00345     d->status = -1;
    00346     d->host = host;
    00347     d->needSSLHandShake = m_bIsSSL;
    00348     p = port(_port);
    00349     ks.setAddress(host, p);
    00350     if ( d->timeout > -1 )
    00351         ks.setTimeout( d->timeout );
    00352
    00353     if (ks.connect() < 0)
    00354     {
    00355         d->status = ks.status();
    00356         if ( sendError )
    00357         {
    00358             if (d->status == IO_LookupError)
    00359                 error( ERR_UNKNOWN_HOST, host);
    00360             else if ( d->status != -1 )
    00361                 error( ERR_COULD_NOT_CONNECT, host);
    00362         }
    00363         return false;
    00364     }
    00365
    00366     m_iSock = ks.fd();
    00367
    00368     // store the IP for later
    00369     const KSocketAddress *sa = ks.peerAddress();
    00370     if (sa)
    00371       d->ip = sa->nodeName();
    00372     else
    00373       d->ip = "";
    00374
    00375     ks.release(); // KExtendedSocket no longer applicable
    00376
    00377     if ( d->block != ks.blockingMode() )
    00378         ks.setBlockingMode( d->block );
    00379
    00380     m_iPort=p;
    00381
    00382     if (m_bIsSSL && !d->useSSLTunneling) {
    00383         if ( !doSSLHandShake( sendError ) )
    00384             return false;
    00385     }
    00386     else
    00387         setMetaData("ssl_in_use", "FALSE");
    00388
    00389     // Since we want to use stdio on the socket,
    00390     // we must fdopen it to get a file pointer,
    00391     // if it fails, close everything up
    00392     if ((fp = KDE_fdopen(m_iSock, "w+")) == 0) {
    00393         closeDescriptor();
    00394         return false;
    00395     }
    00396
    00397     return true;
    00398 #else
    00399     return false;
    00400 #endif //Q_OS_UNIX
    00401 }
    00402
    00403 void TCPSlaveBase::closeDescriptor()
    00404 {
    00405     stopTLS();
    00406     if (fp) {
    00407         fclose(fp);
    00408         fp=0;
    00409         m_iSock=-1;
    00410         if (m_bIsSSL)
    00411             d->kssl->close();
    00412     }
    00413     if (m_iSock != -1) {
    00414         close(m_iSock);
    00415         m_iSock=-1;
    00416     }
    00417     d->ip = "";
    00418     d->host = "";
    00419 }
    00420
    00421 bool TCPSlaveBase::initializeSSL()
    00422 {
    00423     if (m_bIsSSL) {
    00424         if (KSSL::doesSSLWork()) {
    00425             d->kssl = new KSSL;
    00426             return true;
    00427         }
    00428     }
    00429 return false;
    00430 }
    00431
    00432 void TCPSlaveBase::cleanSSL()
    00433 {
    00434     delete d->cc;
    00435
    00436     if (m_bIsSSL) {
    00437         delete d->kssl;
    00438         d->kssl = 0;
    00439     }
    00440     d->militantSSL = false;
    00441 }
    00442
    00443 bool TCPSlaveBase::atEnd()
    00444 {
    00445     return feof(fp);
    00446 }
    00447
    00448 int TCPSlaveBase::startTLS()
    00449 {
    00450     if (d->usingTLS || d->useSSLTunneling || m_bIsSSL || !KSSL::doesSSLWork())
    00451         return false;
    00452
    00453     d->kssl = new KSSL(false);
    00454     if (!d->kssl->TLSInit()) {
    00455         delete d->kssl;
    00456         return -1;
    00457     }
    00458
    00459     if ( !d->realHost.isEmpty() )
    00460     {
    00461       kdDebug(7029) << "Setting real hostname: " << d->realHost << endl;
    00462       d->kssl->setPeerHost(d->realHost);
    00463     } else {
    00464       kdDebug(7029) << "Setting real hostname: " << d->host << endl;
    00465       d->kssl->setPeerHost(d->host);
    00466     }
    00467
    00468     if (hasMetaData("ssl_session_id")) {
    00469         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
    00470         if (s) {
    00471             d->kssl->setSession(s);
    00472             delete s;
    00473         }
    00474     }
    00475     certificatePrompt();
    00476
    00477     int rc = d->kssl->connect(m_iSock);
    00478     if (rc < 0) {
    00479         delete d->kssl;
    00480         return -2;
    00481     }
    00482
    00483     setMetaData("ssl_session_id", d->kssl->session()->toString());
    00484
    00485     d->usingTLS = true;
    00486     setMetaData("ssl_in_use", "TRUE");
    00487
    00488     if (!d->kssl->reusingSession()) {
    00489         rc = verifyCertificate();
    00490         if (rc != 1) {
    00491             setMetaData("ssl_in_use", "FALSE");
    00492             d->usingTLS = false;
    00493             delete d->kssl;
    00494             return -3;
    00495         }
    00496     }
    00497
    00498     d->savedMetaData = mOutgoingMetaData;
    00499     return (d->usingTLS ? 1 : 0);
    00500 }
    00501
    00502
    00503 void TCPSlaveBase::stopTLS()
    00504 {
    00505     if (d->usingTLS) {
    00506         delete d->kssl;
    00507         d->usingTLS = false;
    00508         setMetaData("ssl_in_use", "FALSE");
    00509     }
    00510 }
    00511
    00512
    00513 void TCPSlaveBase::setSSLMetaData() {
    00514   if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL))
    00515     return;
    00516
    00517   mOutgoingMetaData = d->savedMetaData;
    00518 }
    00519
    00520
    00521 bool TCPSlaveBase::canUseTLS()
    00522 {
    00523     if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork())
    00524         return false;
    00525
    00526     KSSLSettings kss;
    00527     return kss.tlsv1();
    00528 }
    00529
    00530
    00531 void TCPSlaveBase::certificatePrompt()
    00532 {
    00533 QString certname;   // the cert to use this session
    00534 bool send = false, prompt = false, save = false, forcePrompt = false;
    00535 KSSLCertificateHome::KSSLAuthAction aa;
    00536
    00537   setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed
    00538
    00539   if (metaData("ssl_no_client_cert") == "TRUE") return;
    00540   forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE");
    00541
    00542   // Delete the old cert since we're certainly done with it now
    00543   if (d->pkcs) {
    00544      delete d->pkcs;
    00545      d->pkcs = NULL;
    00546   }
    00547
    00548   if (!d->kssl) return;
    00549
    00550   // Look for a general certificate
    00551   if (!forcePrompt) {
    00552         certname = KSSLCertificateHome::getDefaultCertificateName(&aa);
    00553         switch(aa) {
    00554         case KSSLCertificateHome::AuthSend:
    00555           send = true; prompt = false;
    00556          break;
    00557         case KSSLCertificateHome::AuthDont:
    00558           send = false; prompt = false;
    00559           certname = QString::null;
    00560          break;
    00561         case KSSLCertificateHome::AuthPrompt:
    00562           send = false; prompt = true;
    00563          break;
    00564         default:
    00565          break;
    00566         }
    00567   }
    00568
    00569   QString ourHost;
    00570   if (!d->realHost.isEmpty()) {
    00571      ourHost = d->realHost;
    00572   } else {
    00573      ourHost = d->host;
    00574   }
    00575
    00576   // Look for a certificate on a per-host basis as an override
    00577   QString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa);
    00578   if (aa != KSSLCertificateHome::AuthNone) {   // we must override
    00579     switch (aa) {
    00580         case KSSLCertificateHome::AuthSend:
    00581           send = true;
    00582           prompt = false;
    00583           certname = tmpcn;
    00584          break;
    00585         case KSSLCertificateHome::AuthDont:
    00586           send = false;
    00587           prompt = false;
    00588           certname = QString::null;
    00589          break;
    00590         case KSSLCertificateHome::AuthPrompt:
    00591           send = false;
    00592           prompt = true;
    00593           certname = tmpcn;
    00594          break;
    00595         default:
    00596          break;
    00597     }
    00598   }
    00599
    00600   // Finally, we allow the application to override anything.
    00601   if (hasMetaData("ssl_demand_certificate")) {
    00602      certname = metaData("ssl_demand_certificate");
    00603      if (!certname.isEmpty()) {
    00604         forcePrompt = false;
    00605         prompt = false;
    00606         send = true;
    00607      }
    00608   }
    00609
    00610   if (certname.isEmpty() && !prompt && !forcePrompt) return;
    00611
    00612   // Ok, we're supposed to prompt the user....
    00613   if (prompt || forcePrompt) {
    00614     QStringList certs = KSSLCertificateHome::getCertificateList();
    00615
    00616     for (QStringList::Iterator it = certs.begin(); it != certs.end(); ++it) {
    00617       KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it);
    00618       if (pkcs && (!pkcs->getCertificate() ||
    00619           !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) {
    00620         certs.remove(*it);
    00621       }
    00622     }
    00623
    00624     if (certs.isEmpty()) return;  // we had nothing else, and prompt failed
    00625
    00626     if (!d->dcc) {
    00627         d->dcc = new DCOPClient;
    00628         d->dcc->attach();
    00629         if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
    00630            KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
    00631                                                    QStringList() );
    00632         }
    00633     }
    00634
    00635      QByteArray data, retval;
    00636      QCString rettype;
    00637      QDataStream arg(data, IO_WriteOnly);
    00638      arg << ourHost;
    00639      arg << certs;
    00640      arg << metaData("window-id").toInt();
    00641      bool rc = d->dcc->call("kio_uiserver", "UIServer",
    00642                                "showSSLCertDialog(QString, QStringList,int)",
    00643                                data, rettype, retval);
    00644
    00645      if (rc && rettype == "KSSLCertDlgRet") {
    00646         QDataStream retStream(retval, IO_ReadOnly);
    00647         KSSLCertDlgRet drc;
    00648         retStream >> drc;
    00649         if (drc.ok) {
    00650            send = drc.send;
    00651            save = drc.save;
    00652            certname = drc.choice;
    00653         }
    00654      }
    00655   }
    00656
    00657   // The user may have said to not send the certificate,
    00658   // but to save the choice
    00659   if (!send) {
    00660      if (save) {
    00661        KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
    00662                                                   false, false);
    00663      }
    00664      return;
    00665   }
    00666
    00667   // We're almost committed.  If we can read the cert, we'll send it now.
    00668   KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname);
    00669   if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) {           // We need the password
    00670      KIO::AuthInfo ai;
    00671      bool first = true;
    00672      do {
    00673         ai.prompt = i18n("Enter the certificate password:");
    00674         ai.caption = i18n("SSL Certificate Password");
    00675         ai.url.setProtocol("kssl");
    00676         ai.url.setHost(certname);
    00677         ai.username = certname;
    00678         ai.keepPassword = true;
    00679
    00680         bool showprompt;
    00681         if (first)
    00682            showprompt = !checkCachedAuthentication(ai);
    00683         else
    00684            showprompt = true;
    00685         if (showprompt) {
    00686            if (!openPassDlg(ai, first ? QString::null :
    00687                    i18n("Unable to open the certificate. Try a new password?")))
    00688               break;
    00689         }
    00690
    00691         first = false;
    00692         pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password);
    00693      } while (!pkcs);
    00694
    00695   }
    00696
    00697    // If we could open the certificate, let's send it
    00698    if (pkcs) {
    00699       if (!d->kssl->setClientCertificate(pkcs)) {
    00700             messageBox(Information, i18n("The procedure to set the "
    00701                                          "client certificate for the session "
    00702                                          "failed."), i18n("SSL"));
    00703          delete pkcs;  // we don't need this anymore
    00704          pkcs = 0L;
    00705       } else {
    00706          kdDebug(7029) << "Client SSL certificate is being used." << endl;
    00707          setMetaData("ssl_using_client_cert", "TRUE");
    00708          if (save) {
    00709                 KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
    00710                                                            true, false);
    00711          }
    00712       }
    00713       d->pkcs = pkcs;
    00714    }
    00715 }
    00716
    00717
    00718
    00719 bool TCPSlaveBase::usingTLS() const
    00720 {
    00721     return d->usingTLS;
    00722 }
    00723
    00724 // ### remove this for KDE4 (misses const):
    00725 bool TCPSlaveBase::usingTLS()
    00726 {
    00727     return d->usingTLS;
    00728 }
    00729
    00730
    00731 //  Returns 0 for failed verification, -1 for rejected cert and 1 for ok
    00732 int TCPSlaveBase::verifyCertificate()
    00733 {
    00734     int rc = 0;
    00735     bool permacache = false;
    00736     bool isChild = false;
    00737     bool _IPmatchesCN = false;
    00738     int result;
    00739     bool doAddHost = false;
    00740     QString ourHost;
    00741
    00742     if (!d->realHost.isEmpty())
    00743         ourHost = d->realHost;
    00744     else ourHost = d->host;
    00745
    00746     QString theurl = QString(m_sServiceName)+"://"+ourHost+":"+QString::number(m_iPort);
    00747
    00748    if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE")
    00749      d->militantSSL = false;
    00750    else if (metaData("ssl_militant") == "TRUE")
    00751      d->militantSSL = true;
    00752
    00753     if (!d->cc) d->cc = new KSSLCertificateCache;
    00754
    00755     KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate();
    00756
    00757     KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer);
    00758
    00759    _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
    00760    if (!_IPmatchesCN && !d->militantSSL) {  // force this if the user wants it
    00761       if (d->cc->getHostList(pc).contains(ourHost))
    00762          _IPmatchesCN = true;
    00763    }
    00764
    00765    if (!_IPmatchesCN)
    00766    {
    00767       ksvl << KSSLCertificate::InvalidHost;
    00768    }
    00769
    00770    KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok;
    00771    if (!ksvl.isEmpty())
    00772       ksv = ksvl.first();
    00773
    00774     /* Setting the various bits of meta-info that will be needed. */
    00775     setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher());
    00776     setMetaData("ssl_cipher_desc",
    00777                             d->kssl->connectionInfo().getCipherDescription());
    00778     setMetaData("ssl_cipher_version",
    00779                                 d->kssl->connectionInfo().getCipherVersion());
    00780     setMetaData("ssl_cipher_used_bits",
    00781               QString::number(d->kssl->connectionInfo().getCipherUsedBits()));
    00782     setMetaData("ssl_cipher_bits",
    00783                   QString::number(d->kssl->connectionInfo().getCipherBits()));
    00784     setMetaData("ssl_peer_ip", d->ip);
    00785
    00786     QString errorStr;
    00787     for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
    00788         it != ksvl.end(); ++it)
    00789     {
    00790        errorStr += QString::number(*it)+":";
    00791     }
    00792     setMetaData("ssl_cert_errors", errorStr);
    00793     setMetaData("ssl_peer_certificate", pc.toString());
    00794
    00795     if (pc.chain().isValid() && pc.chain().depth() > 1) {
    00796        QString theChain;
    00797        QPtrList<KSSLCertificate> chain = pc.chain().getChain();
    00798        for (KSSLCertificate *c = chain.first(); c; c = chain.next()) {
    00799           theChain += c->toString();
    00800           theChain += "\n";
    00801        }
    00802        setMetaData("ssl_peer_chain", theChain);
    00803     } else setMetaData("ssl_peer_chain", "");
    00804
    00805    setMetaData("ssl_cert_state", QString::number(ksv));
    00806
    00807    if (ksv == KSSLCertificate::Ok) {
    00808       rc = 1;
    00809       setMetaData("ssl_action", "accept");
    00810    }
    00811
    00812    kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl;
    00813    if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
    00814       // Since we're the parent, we need to teach the child.
    00815       setMetaData("ssl_parent_ip", d->ip);
    00816       setMetaData("ssl_parent_cert", pc.toString());
    00817       //  - Read from cache and see if there is a policy for this
    00818       KSSLCertificateCache::KSSLCertificatePolicy cp =
    00819                                          d->cc->getPolicyByCertificate(pc);
    00820
    00821       //  - validation code
    00822       if (ksv != KSSLCertificate::Ok) {
    00823          if (d->militantSSL) {
    00824             return -1;
    00825          }
    00826
    00827          if (cp == KSSLCertificateCache::Unknown ||
    00828              cp == KSSLCertificateCache::Ambiguous) {
    00829             cp = KSSLCertificateCache::Prompt;
    00830          } else {
    00831             // A policy was already set so let's honor that.
    00832             permacache = d->cc->isPermanent(pc);
    00833          }
    00834
    00835          if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
    00836             cp = KSSLCertificateCache::Prompt;
    00837 //            ksv = KSSLCertificate::Ok;
    00838          }
    00839
    00840          // Precondition: cp is one of Reject, Accept or Prompt
    00841          switch (cp) {
    00842          case KSSLCertificateCache::Accept:
    00843            rc = 1;
    00844            setMetaData("ssl_action", "accept");
    00845           break;
    00846          case KSSLCertificateCache::Reject:
    00847            rc = -1;
    00848            setMetaData("ssl_action", "reject");
    00849           break;
    00850          case KSSLCertificateCache::Prompt:
    00851            {
    00852              do {
    00853                 if (ksv == KSSLCertificate::InvalidHost) {
    00854                         QString msg = i18n("The IP address of the host %1 "
    00855                                            "does not match the one the "
    00856                                            "certificate was issued to.");
    00857                    result = messageBox( WarningYesNoCancel,
    00858                               msg.arg(ourHost),
    00859                               i18n("Server Authentication"),
    00860                               i18n("&Details"),
    00861                               i18n("Co&ntinue") );
    00862                 } else {
    00863                    QString msg = i18n("The server certificate failed the "
    00864                                       "authenticity test (%1).");
    00865                    result = messageBox( WarningYesNoCancel,
    00866                               msg.arg(ourHost),
    00867                               i18n("Server Authentication"),
    00868                               i18n("&Details"),
    00869                               i18n("Co&ntinue") );
    00870                 }
    00871
    00872                 if (result == KMessageBox::Yes) {
    00873                    if (!d->dcc) {
    00874                       d->dcc = new DCOPClient;
    00875                       d->dcc->attach();
    00876                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
    00877                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
    00878                          QStringList() );
    00879                       }
    00880
    00881                    }
    00882                    QByteArray data, ignore;
    00883                    QCString ignoretype;
    00884                    QDataStream arg(data, IO_WriteOnly);
    00885                    arg << theurl << mOutgoingMetaData;
    00886                    arg << metaData("window-id").toInt();
    00887                         d->dcc->call("kio_uiserver", "UIServer",
    00888                                 "showSSLInfoDialog(QString,KIO::MetaData,int)",
    00889                                 data, ignoretype, ignore);
    00890                 }
    00891              } while (result == KMessageBox::Yes);
    00892
    00893              if (result == KMessageBox::No) {
    00894                 setMetaData("ssl_action", "accept");
    00895                 rc = 1;
    00896                 cp = KSSLCertificateCache::Accept;
    00897                 doAddHost = true;
    00898                    result = messageBox( WarningYesNo,
    00899                                   i18n("Would you like to accept this "
    00900                                        "certificate forever without "
    00901                                        "being prompted?"),
    00902                                   i18n("Server Authentication"),
    00903                                          i18n("&Forever"),
    00904                                          i18n("&Current Sessions Only"));
    00905                     if (result == KMessageBox::Yes)
    00906                         permacache = true;
    00907                     else
    00908                         permacache = false;
    00909              } else {
    00910                 setMetaData("ssl_action", "reject");
    00911                 rc = -1;
    00912                 cp = KSSLCertificateCache::Prompt;
    00913              }
    00914           break;
    00915             }
    00916          default:
    00917           kdDebug(7029) << "TCPSlaveBase/SSL error in cert code."
    00918                               << "Please report this to kfm-devel@kde.org."
    00919                               << endl;
    00920           break;
    00921          }
    00922       }
    00923
    00924
    00925       //  - cache the results
    00926       d->cc->addCertificate(pc, cp, permacache);
    00927       if (doAddHost) d->cc->addHost(pc, ourHost);
    00928     } else {    // Child frame
    00929       //  - Read from cache and see if there is a policy for this
    00930       KSSLCertificateCache::KSSLCertificatePolicy cp =
    00931                                              d->cc->getPolicyByCertificate(pc);
    00932       isChild = true;
    00933
    00934       // Check the cert and IP to make sure they're the same
    00935       // as the parent frame
    00936       bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
    00937                                pc.toString() == metaData("ssl_parent_cert"));
    00938
    00939       if (ksv == KSSLCertificate::Ok) {
    00940         if (certAndIPTheSame) {       // success
    00941           rc = 1;
    00942           setMetaData("ssl_action", "accept");
    00943         } else {
    00944           /*
    00945           if (d->militantSSL) {
    00946             return -1;
    00947           }
    00948           result = messageBox(WarningYesNo,
    00949                               i18n("The certificate is valid but does not appear to have been assigned to this server.  Do you wish to continue loading?"),
    00950                               i18n("Server Authentication"));
    00951           if (result == KMessageBox::Yes) {     // success
    00952             rc = 1;
    00953             setMetaData("ssl_action", "accept");
    00954           } else {    // fail
    00955             rc = -1;
    00956             setMetaData("ssl_action", "reject");
    00957           }
    00958           */
    00959           setMetaData("ssl_action", "accept");
    00960           rc = 1;   // Let's accept this now.  It's bad, but at least the user
    00961                     // will see potential attacks in KDE3 with the pseudo-lock
    00962                     // icon on the toolbar, and can investigate with the RMB
    00963         }
    00964       } else {
    00965         if (d->militantSSL) {
    00966           return -1;
    00967         }
    00968
    00969         if (cp == KSSLCertificateCache::Accept) {
    00970            if (certAndIPTheSame) {    // success
    00971              rc = 1;
    00972              setMetaData("ssl_action", "accept");
    00973            } else {   // fail
    00974              result = messageBox(WarningYesNo,
    00975                                  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?"),
    00976                                  i18n("Server Authentication"));
    00977              if (result == KMessageBox::Yes) {
    00978                rc = 1;
    00979                setMetaData("ssl_action", "accept");
    00980                d->cc->addHost(pc, ourHost);
    00981              } else {
    00982                rc = -1;
    00983                setMetaData("ssl_action", "reject");
    00984              }
    00985            }
    00986         } else if (cp == KSSLCertificateCache::Reject) {      // fail
    00987           messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE Control Center."),
    00988                                   i18n("Server Authentication"));
    00989           rc = -1;
    00990           setMetaData("ssl_action", "reject");
    00991         } else {
    00992           do {
    00993              QString msg = i18n("The server certificate failed the "
    00994                                 "authenticity test (%1).");
    00995              result = messageBox(WarningYesNoCancel,
    00996                                  msg.arg(ourHost),
    00997                                  i18n("Server Authentication"),
    00998                                  i18n("&Details"),
    00999                                  i18n("Co&nnect"));
    01000                 if (result == KMessageBox::Yes) {
    01001                    if (!d->dcc) {
    01002                       d->dcc = new DCOPClient;
    01003                       d->dcc->attach();
    01004                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
    01005                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
    01006                          QStringList() );
    01007                       }
    01008                    }
    01009                    QByteArray data, ignore;
    01010                    QCString ignoretype;
    01011                    QDataStream arg(data, IO_WriteOnly);
    01012                    arg << theurl << mOutgoingMetaData;
    01013                    arg << metaData("window-id").toInt();
    01014                         d->dcc->call("kio_uiserver", "UIServer",
    01015                                 "showSSLInfoDialog(QString,KIO::MetaData,int)",
    01016                                 data, ignoretype, ignore);
    01017                 }
    01018           } while (result == KMessageBox::Yes);
    01019
    01020           if (result == KMessageBox::No) {
    01021              setMetaData("ssl_action", "accept");
    01022              rc = 1;
    01023              cp = KSSLCertificateCache::Accept;
    01024              result = messageBox(WarningYesNo,
    01025                                  i18n("Would you like to accept this "
    01026                                       "certificate forever without "
    01027                                       "being prompted?"),
    01028                                  i18n("Server Authentication"),
    01029                                  i18n("&Forever"),
    01030                                  i18n("&Current Sessions Only"));
    01031              permacache = (result == KMessageBox::Yes);
    01032              d->cc->addCertificate(pc, cp, permacache);
    01033              d->cc->addHost(pc, ourHost);
    01034           } else {
    01035              setMetaData("ssl_action", "reject");
    01036              rc = -1;
    01037              cp = KSSLCertificateCache::Prompt;
    01038              d->cc->addCertificate(pc, cp, permacache);
    01039           }
    01040         }
    01041       }
    01042     }
    01043
    01044
    01045    if (rc == -1) {
    01046       return rc;
    01047    }
    01048
    01049    if (metaData("ssl_activate_warnings") == "TRUE") {
    01050    //  - entering SSL
    01051    if (!isChild && metaData("ssl_was_in_use") == "FALSE" &&
    01052                                         d->kssl->settings()->warnOnEnter()) {
    01053      int result;
    01054      do {
    01055                 result = messageBox(               i18n("You are about to "
    01056                                                         "enter secure mode. "
    01057                                                         "All transmissions "
    01058                                                         "will be encrypted "
    01059                                                         "unless otherwise "
    01060                                                         "noted.\nThis means "
    01061                                                         "that no third party "
    01062                                                         "will be able to "
    01063                                                         "easily observe your "
    01064                                                         "data in transit."),
    01065                                                    WarningYesNo,
    01066                                                    i18n("Security Information"),
    01067                                                    i18n("Display SSL "
    01068                                                         "&Information"),
    01069                                                    i18n("C&onnect"),
    01070                                                    "WarnOnEnterSSLMode" );
    01071       // Move this setting into KSSL instead
    01072       KConfig *config = new KConfig("kioslaverc");
    01073       config->setGroup("Notification Messages");
    01074
    01075       if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) {
    01076           config->deleteEntry("WarnOnEnterSSLMode");
    01077           config->sync();
    01078           d->kssl->settings()->setWarnOnEnter(false);
    01079           d->kssl->settings()->save();
    01080       }
    01081       delete config;
    01082
    01083       if ( result == KMessageBox::Yes )
    01084       {
    01085           if (!d->dcc) {
    01086              d->dcc = new DCOPClient;
    01087              d->dcc->attach();
    01088              if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
    01089                 KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
    01090                 QStringList() );
    01091              }
    01092           }
    01093           QByteArray data, ignore;
    01094           QCString ignoretype;
    01095           QDataStream arg(data, IO_WriteOnly);
    01096           arg << theurl << mOutgoingMetaData;
    01097           arg << metaData("window-id").toInt();
    01098           d->dcc->call("kio_uiserver", "UIServer",
    01099                        "showSSLInfoDialog(QString,KIO::MetaData,int)",
    01100                        data, ignoretype, ignore);
    01101       }
    01102       } while (result != KMessageBox::No);
    01103    }
    01104
    01105    }   // if ssl_activate_warnings
    01106
    01107
    01108    kdDebug(7029) << "SSL connection information follows:" << endl
    01109           << "+-----------------------------------------------" << endl
    01110           << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
    01111           << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
    01112           << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
    01113           << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
    01114           << " of " << d->kssl->connectionInfo().getCipherBits()
    01115           << " bits used." << endl
    01116           << "| PEER:" << endl
    01117           << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
    01118           << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
    01119           << "| Validation: " << (int)ksv << endl
    01120           << "| Certificate matches IP: " << _IPmatchesCN << endl
    01121           << "+-----------------------------------------------"
    01122           << endl;
    01123
    01124    // sendMetaData();  Do not call this function!!
    01125    return rc;
    01126 }
    01127
    01128
    01129 bool TCPSlaveBase::isConnectionValid()
    01130 {
    01131     if ( m_iSock == -1 )
    01132       return false;
    01133
    01134     fd_set rdfs;
    01135     FD_ZERO(&rdfs);
    01136     FD_SET(m_iSock , &rdfs);
    01137
    01138     struct timeval tv;
    01139     tv.tv_usec = 0;
    01140     tv.tv_sec = 0;
    01141     int retval;
    01142 #ifdef Q_OS_UNIX
    01143     do {
    01144        retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
    01145        if (wasKilled())
    01146           return false; // Beam us out of here
    01147     } while ((retval == -1) && (errno == EAGAIN));
    01148 #else
    01149     retval = -1;
    01150 #endif
    01151     // retval == -1 ==> Error
    01152     // retval ==  0 ==> Connection Idle
    01153     // retval >=  1 ==> Connection Active
    01154     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
    01155     //              << retval << endl;
    01156
    01157     if (retval == -1)
    01158        return false;
    01159
    01160     if (retval == 0)
    01161        return true;
    01162
    01163     // Connection is active, check if it has closed.
    01164     char buffer[100];
    01165 #ifdef Q_OS_UNIX
    01166     do {
    01167        retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
    01168
    01169     } while ((retval == -1) && (errno == EAGAIN));
    01170 #else
    01171     retval = -1;
    01172 #endif
    01173     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
    01174     //                 << retval << endl;
    01175     if (retval <= 0)
    01176        return false; // Error or connection closed.
    01177
    01178     return true; // Connection still valid.
    01179 }
    01180
    01181
    01182 bool TCPSlaveBase::waitForResponse( int t )
    01183 {
    01184   fd_set rd;
    01185   struct timeval timeout;
    01186
    01187   if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
    01188     if (d->kssl->pending() > 0)
    01189         return true;
    01190
    01191   FD_ZERO(&rd);
    01192   FD_SET(m_iSock, &rd);
    01193
    01194   timeout.tv_usec = 0;
    01195   timeout.tv_sec = t;
    01196   time_t startTime;
    01197
    01198   int rc;
    01199   int n = t;
    01200
    01201 reSelect:
    01202   startTime = time(NULL);
    01203 #ifdef Q_OS_UNIX
    01204   rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
    01205 #else
    01206   rc = -1;
    01207 #endif
    01208   if (wasKilled())
    01209     return false; // We're dead.
    01210
    01211   if (rc == -1)
    01212     return false;
    01213
    01214   if (FD_ISSET(m_iSock, &rd))
    01215     return true;
    01216
    01217   // Well it returned but it wasn't set.  Let's see if it
    01218   // returned too early (perhaps from an errant signal) and
    01219   // start over with the remaining time
    01220   int timeDone = time(NULL) - startTime;
    01221   if (timeDone < n)
    01222   {
    01223     n -= timeDone;
    01224     timeout.tv_sec = n;
    01225     goto reSelect;
    01226   }
    01227
    01228   return false; // Timed out!
    01229 }
    01230
    01231 int TCPSlaveBase::connectResult()
    01232 {
    01233     return d->status;
    01234 }
    01235
    01236 void TCPSlaveBase::setBlockConnection( bool b )
    01237 {
    01238     d->block = b;
    01239 }
    01240
    01241 void TCPSlaveBase::setConnectTimeout( int t )
    01242 {
    01243     d->timeout = t;
    01244 }
    01245
    01246 bool TCPSlaveBase::isSSLTunnelEnabled()
    01247 {
    01248     return d->useSSLTunneling;
    01249 }
    01250
    01251 void TCPSlaveBase::setEnableSSLTunnel( bool enable )
    01252 {
    01253     d->useSSLTunneling = enable;
    01254 }
    01255
    01256 void TCPSlaveBase::setRealHost( const QString& realHost )
    01257 {
    01258     d->realHost = realHost;
    01259 }
    01260
    01261 bool TCPSlaveBase::doSSLHandShake( bool sendError )
    01262 {
    01263     kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
    01264     QString msgHost = d->host;
    01265
    01266     d->kssl->reInitialize();
    01267
    01268     if (hasMetaData("ssl_session_id")) {
    01269         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
    01270         if (s) {
    01271             d->kssl->setSession(s);
    01272             delete s;
    01273     }
    01274     }
    01275     certificatePrompt();
    01276
    01277     if ( !d->realHost.isEmpty() )
    01278     {
    01279       msgHost = d->realHost;
    01280     }
    01281
    01282     kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
    01283     d->kssl->setPeerHost(msgHost);
    01284
    01285     d->status = d->kssl->connect(m_iSock);
    01286     if (d->status < 0)
    01287     {
    01288         closeDescriptor();
    01289         if ( sendError )
    01290             error( ERR_COULD_NOT_CONNECT, msgHost);
    01291         return false;
    01292     }
    01293
    01294     setMetaData("ssl_session_id", d->kssl->session()->toString());
    01295     setMetaData("ssl_in_use", "TRUE");
    01296
    01297     if (!d->kssl->reusingSession()) {
    01298         int rc = verifyCertificate();
    01299         if ( rc != 1 ) {
    01300             d->status = -1;
    01301             closeDescriptor();
    01302             if ( sendError )
    01303                 error( ERR_COULD_NOT_CONNECT, msgHost);
    01304             return false;
    01305         }
    01306     }
    01307
    01308     d->needSSLHandShake = false;
    01309
    01310     d->savedMetaData = mOutgoingMetaData;
    01311     return true;
    01312 }
    01313
    01314
    01315 bool TCPSlaveBase::userAborted() const
    01316 {
    01317    return d->userAborted;
    01318 }
    01319
    01320 void TCPSlaveBase::virtual_hook( int id, void* data )
    01321 { SlaveBase::virtual_hook( id, data ); }
    01322

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:08 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003