kio Library API Documentation

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 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:33 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001