# kio Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# tcpslavebase.cpp

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

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:14:42 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001