kio Library API Documentation

tcpslavebase.cpp

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