kio Library API Documentation

tcpslavebase.cpp

00001 /*
00002  * $Id: tcpslavebase.cpp,v 1.134 2004/06/27 10:44:42 binner 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( i18n("You are about to leave secure "
00303                                         "mode. Transmissions will no "
00304                                         "longer be encrypted.\nThis "
00305                                         "means that a third party could "
00306                                         "observe your data in transit."),
00307                                    WarningContinueCancel,
00308                                    i18n("Security Information"),
00309                                    i18n("C&ontinue Loading"), QString::null,
00310                                    "WarnOnLeaveSSLMode" );
00311 
00312            // Move this setting into KSSL instead
00313           KConfig *config = new KConfig("kioslaverc");
00314           config->setGroup("Notification Messages");
00315 
00316           if (!config->readBoolEntry("WarnOnLeaveSSLMode", true)) {
00317               config->deleteEntry("WarnOnLeaveSSLMode");
00318               config->sync();
00319               kss.setWarnOnLeave(false);
00320               kss.save();
00321           }
00322           delete config;
00323 
00324           if ( result == KMessageBox::Cancel ) {
00325              d->userAborted = true;
00326              return false;
00327           }
00328        }
00329     }
00330 
00331     d->status = -1;
00332     d->host = host;
00333     d->needSSLHandShake = m_bIsSSL;
00334     p = port(_port);
00335     ks.setAddress(host, p);
00336     if ( d->timeout > -1 )
00337         ks.setTimeout( d->timeout );
00338 
00339     if (ks.connect() < 0)
00340     {
00341         d->status = ks.status();
00342         if ( sendError )
00343         {
00344             if (d->status == IO_LookupError)
00345                 error( ERR_UNKNOWN_HOST, host);
00346             else if ( d->status != -1 )
00347                 error( ERR_COULD_NOT_CONNECT, host);
00348         }
00349         return false;
00350     }
00351 
00352     m_iSock = ks.fd();
00353 
00354     // store the IP for later
00355     const KSocketAddress *sa = ks.peerAddress();
00356     if (sa)
00357       d->ip = sa->nodeName();
00358     else
00359       d->ip = "";
00360 
00361     ks.release(); // KExtendedSocket no longer applicable
00362 
00363     if ( d->block != ks.blockingMode() )
00364         ks.setBlockingMode( d->block );
00365 
00366     m_iPort=p;
00367 
00368     if (m_bIsSSL && !d->useSSLTunneling) {
00369         if ( !doSSLHandShake( sendError ) )
00370             return false;
00371     }
00372     else
00373         setMetaData("ssl_in_use", "FALSE");
00374 
00375     // Since we want to use stdio on the socket,
00376     // we must fdopen it to get a file pointer,
00377     // if it fails, close everything up
00378     if ((fp = fdopen(m_iSock, "w+")) == 0) {
00379         closeDescriptor();
00380         return false;
00381     }
00382 
00383     return true;
00384 }
00385 
00386 void TCPSlaveBase::closeDescriptor()
00387 {
00388     stopTLS();
00389     if (fp) {
00390         fclose(fp);
00391         fp=0;
00392         m_iSock=-1;
00393         if (m_bIsSSL)
00394             d->kssl->close();
00395     }
00396     if (m_iSock != -1) {
00397         close(m_iSock);
00398         m_iSock=-1;
00399     }
00400     d->ip = "";
00401     d->host = "";
00402 }
00403 
00404 bool TCPSlaveBase::initializeSSL()
00405 {
00406     if (m_bIsSSL) {
00407         if (KSSL::doesSSLWork()) {
00408             d->kssl = new KSSL;
00409             return true;
00410         }
00411     }
00412 return false;
00413 }
00414 
00415 void TCPSlaveBase::cleanSSL()
00416 {
00417     delete d->cc;
00418 
00419     if (m_bIsSSL) {
00420         delete d->kssl;
00421         d->kssl = 0;
00422     }
00423     d->militantSSL = false;
00424 }
00425 
00426 bool TCPSlaveBase::atEnd()
00427 {
00428     return feof(fp);
00429 }
00430 
00431 int TCPSlaveBase::startTLS()
00432 {
00433     if (d->usingTLS || d->useSSLTunneling || m_bIsSSL || !KSSL::doesSSLWork())
00434         return false;
00435 
00436     d->kssl = new KSSL(false);
00437     if (!d->kssl->TLSInit()) {
00438         delete d->kssl;
00439         return -1;
00440     }
00441 
00442     if ( !d->realHost.isEmpty() )
00443     {
00444       kdDebug(7029) << "Setting real hostname: " << d->realHost << endl;
00445       d->kssl->setPeerHost(d->realHost);
00446     } else {
00447       kdDebug(7029) << "Setting real hostname: " << d->host << endl;
00448       d->kssl->setPeerHost(d->host);
00449     }
00450 
00451     if (hasMetaData("ssl_session_id")) {
00452         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
00453         if (s) {
00454             d->kssl->setSession(s);
00455             delete s;
00456         }
00457     }
00458     certificatePrompt();
00459 
00460     int rc = d->kssl->connect(m_iSock);
00461     if (rc < 0) {
00462         delete d->kssl;
00463         return -2;
00464     }
00465 
00466     setMetaData("ssl_session_id", d->kssl->session()->toString());
00467 
00468     d->usingTLS = true;
00469     setMetaData("ssl_in_use", "TRUE");
00470 
00471     if (!d->kssl->reusingSession()) {
00472         rc = verifyCertificate();
00473         if (rc != 1) {
00474             setMetaData("ssl_in_use", "FALSE");
00475             d->usingTLS = false;
00476             delete d->kssl;
00477             return -3;
00478         }
00479     }
00480 
00481     d->savedMetaData = mOutgoingMetaData;
00482     return (d->usingTLS ? 1 : 0);
00483 }
00484 
00485 
00486 void TCPSlaveBase::stopTLS()
00487 {
00488     if (d->usingTLS) {
00489         delete d->kssl;
00490         d->usingTLS = false;
00491         setMetaData("ssl_in_use", "FALSE");
00492     }
00493 }
00494 
00495 
00496 void TCPSlaveBase::setSSLMetaData() {
00497   if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL))
00498     return;
00499 
00500   mOutgoingMetaData = d->savedMetaData;
00501 }
00502 
00503 
00504 bool TCPSlaveBase::canUseTLS()
00505 {
00506     if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork())
00507         return false;
00508 
00509     KSSLSettings kss;
00510     return kss.tlsv1();
00511 }
00512 
00513 
00514 void TCPSlaveBase::certificatePrompt()
00515 {
00516 QString certname;   // the cert to use this session
00517 bool send = false, prompt = false, save = false, forcePrompt = false;
00518 KSSLCertificateHome::KSSLAuthAction aa;
00519 
00520   setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed
00521 
00522   if (metaData("ssl_no_client_cert") == "TRUE") return;
00523   forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE");
00524 
00525   // Delete the old cert since we're certainly done with it now
00526   if (d->pkcs) {
00527      delete d->pkcs;
00528      d->pkcs = NULL;
00529   }
00530 
00531   if (!d->kssl) return;
00532 
00533   // Look for a general certificate
00534   if (!forcePrompt) {
00535         certname = KSSLCertificateHome::getDefaultCertificateName(&aa);
00536         switch(aa) {
00537         case KSSLCertificateHome::AuthSend:
00538           send = true; prompt = false;
00539          break;
00540         case KSSLCertificateHome::AuthDont:
00541           send = false; prompt = false;
00542           certname = QString::null;
00543          break;
00544         case KSSLCertificateHome::AuthPrompt:
00545           send = false; prompt = true;
00546          break;
00547         default:
00548          break;
00549         }
00550   }
00551 
00552   QString ourHost;
00553   if (!d->realHost.isEmpty()) {
00554      ourHost = d->realHost;
00555   } else {
00556      ourHost = d->host;
00557   }
00558 
00559   // Look for a certificate on a per-host basis as an override
00560   QString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa);
00561   if (aa != KSSLCertificateHome::AuthNone) {   // we must override
00562     switch (aa) {
00563         case KSSLCertificateHome::AuthSend:
00564           send = true;
00565           prompt = false;
00566           certname = tmpcn;
00567          break;
00568         case KSSLCertificateHome::AuthDont:
00569           send = false;
00570           prompt = false;
00571           certname = QString::null;
00572          break;
00573         case KSSLCertificateHome::AuthPrompt:
00574           send = false;
00575           prompt = true;
00576           certname = tmpcn;
00577          break;
00578         default:
00579          break;
00580     }
00581   }
00582 
00583   // Finally, we allow the application to override anything.
00584   if (hasMetaData("ssl_demand_certificate")) {
00585      certname = metaData("ssl_demand_certificate");
00586      if (!certname.isEmpty()) {
00587         forcePrompt = false;
00588         prompt = false;
00589         send = true;
00590      }
00591   }
00592 
00593   if (certname.isEmpty() && !prompt && !forcePrompt) return;
00594 
00595   // Ok, we're supposed to prompt the user....
00596   if (prompt || forcePrompt) {
00597     QStringList certs = KSSLCertificateHome::getCertificateList();
00598 
00599     for (QStringList::Iterator it = certs.begin(); it != certs.end(); ++it) {
00600       KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it);
00601       if (pkcs && (!pkcs->getCertificate() ||
00602           !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) {
00603         certs.remove(*it);
00604       }
00605     }
00606 
00607     if (certs.isEmpty()) return;  // we had nothing else, and prompt failed
00608 
00609     if (!d->dcc) {
00610         d->dcc = new DCOPClient;
00611         d->dcc->attach();
00612         if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
00613            KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
00614                                                    QStringList() );
00615         }
00616     }
00617 
00618      QByteArray data, retval;
00619      QCString rettype;
00620      QDataStream arg(data, IO_WriteOnly);
00621      arg << ourHost;
00622      arg << certs;
00623      bool rc = d->dcc->call("kio_uiserver", "UIServer",
00624                                "showSSLCertDialog(QString, QStringList)",
00625                                data, rettype, retval);
00626 
00627      if (rc && rettype == "KSSLCertDlgRet") {
00628         QDataStream retStream(retval, IO_ReadOnly);
00629         KSSLCertDlgRet drc;
00630         retStream >> drc;
00631         if (drc.ok) {
00632            send = drc.send;
00633            save = drc.save;
00634            certname = drc.choice;
00635         }
00636      }
00637   }
00638 
00639   // The user may have said to not send the certificate,
00640   // but to save the choice
00641   if (!send) {
00642      if (save) {
00643        KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
00644                                                   false, false);
00645      }
00646      return;
00647   }
00648 
00649   // We're almost committed.  If we can read the cert, we'll send it now.
00650   KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname);
00651   if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) {           // We need the password
00652      KIO::AuthInfo ai;
00653      bool showprompt = !checkCachedAuthentication(ai);
00654      do {
00655         QString pass;
00656         QByteArray authdata, authval;
00657         QCString rettype;
00658         QDataStream qds(authdata, IO_WriteOnly);
00659         ai.prompt = i18n("Enter the certificate password:");
00660         ai.caption = i18n("SSL Certificate Password");
00661         ai.setModified(true);
00662         ai.username = certname;
00663         ai.keepPassword = true;
00664         if (showprompt) {
00665            qds << ai;
00666 
00667            if (!d->dcc) {
00668               d->dcc = new DCOPClient;
00669               d->dcc->attach();
00670               if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
00671                  KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
00672                                                          QStringList() );
00673              }
00674            }
00675 
00676            bool rc = d->dcc->call("kio_uiserver", "UIServer",
00677                                    "openPassDlg(KIO::AuthInfo)",
00678                                    authdata, rettype, authval);
00679            if (!rc) {
00680              break;
00681            }
00682            if (rettype != "QByteArray") {
00683              continue;
00684            }
00685 
00686            QDataStream qdret(authval, IO_ReadOnly);
00687            QByteArray authdecode;
00688            qdret >> authdecode;
00689            QDataStream qdtoo(authdecode, IO_ReadOnly);
00690            qdtoo >> ai;
00691            if (!ai.isModified()) {
00692              break;
00693            }
00694         }
00695         pass = ai.password;
00696         pkcs = KSSLCertificateHome::getCertificateByName(certname, pass);
00697 
00698         if (!pkcs) {
00699               int rc = messageBox(WarningYesNo, i18n("Unable to open the "
00700                                                      "certificate. Try a "
00701                                                      "new password?"),
00702                                                 i18n("SSL"));
00703               if (rc == KMessageBox::No) {
00704                 break;
00705               }
00706               showprompt = true;
00707         }
00708      } while (!pkcs);
00709      if (pkcs) {
00710        cacheAuthentication(ai);
00711      }
00712   }
00713 
00714    // If we could open the certificate, let's send it
00715    if (pkcs) {
00716       if (!d->kssl->setClientCertificate(pkcs)) {
00717             messageBox(Information, i18n("The procedure to set the "
00718                                          "client certificate for the session "
00719                                          "failed."), i18n("SSL"));
00720          delete pkcs;  // we don't need this anymore
00721          pkcs = 0L;
00722       } else {
00723          kdDebug(7029) << "Client SSL certificate is being used." << endl;
00724          setMetaData("ssl_using_client_cert", "TRUE");
00725          if (save) {
00726                 KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
00727                                                            true, false);
00728          }
00729       }
00730       d->pkcs = pkcs;
00731    }
00732 }
00733 
00734 
00735 
00736 bool TCPSlaveBase::usingTLS() const
00737 {
00738     return d->usingTLS;
00739 }
00740 
00741 // ### remove this for KDE4 (misses const):
00742 bool TCPSlaveBase::usingTLS()
00743 {
00744     return d->usingTLS;
00745 }
00746 
00747 
00748 //  Returns 0 for failed verification, -1 for rejected cert and 1 for ok
00749 int TCPSlaveBase::verifyCertificate()
00750 {
00751     int rc = 0;
00752     bool permacache = false;
00753     bool isChild = false;
00754     bool _IPmatchesCN = false;
00755     int result;
00756     bool doAddHost = false;
00757     QString ourHost;
00758 
00759     if (!d->realHost.isEmpty())
00760         ourHost = d->realHost;
00761     else ourHost = d->host;
00762 
00763     QString theurl = QString(m_sServiceName)+"://"+ourHost+":"+QString::number(m_iPort);
00764 
00765    if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE")
00766      d->militantSSL = false;
00767    else if (metaData("ssl_militant") == "TRUE")
00768      d->militantSSL = true;
00769 
00770     if (!d->cc) d->cc = new KSSLCertificateCache;
00771 
00772     KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate();
00773 
00774     KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer);
00775 
00776    _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
00777    if (!_IPmatchesCN && !d->militantSSL) {  // force this if the user wants it
00778       if (d->cc->getHostList(pc).contains(ourHost))
00779          _IPmatchesCN = true;
00780    }
00781 
00782    if (!_IPmatchesCN)
00783    {
00784       ksvl << KSSLCertificate::InvalidHost;
00785    }
00786 
00787    KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok;
00788    if (!ksvl.isEmpty())
00789       ksv = ksvl.first();
00790 
00791     /* Setting the various bits of meta-info that will be needed. */
00792     setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher());
00793     setMetaData("ssl_cipher_desc",
00794                             d->kssl->connectionInfo().getCipherDescription());
00795     setMetaData("ssl_cipher_version",
00796                                 d->kssl->connectionInfo().getCipherVersion());
00797     setMetaData("ssl_cipher_used_bits",
00798               QString::number(d->kssl->connectionInfo().getCipherUsedBits()));
00799     setMetaData("ssl_cipher_bits",
00800                   QString::number(d->kssl->connectionInfo().getCipherBits()));
00801     setMetaData("ssl_peer_ip", d->ip);
00802     
00803     QString errorStr;
00804     for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
00805         it != ksvl.end(); ++it)
00806     {
00807        errorStr += QString::number(*it)+":";
00808     }
00809     setMetaData("ssl_cert_errors", errorStr);
00810     setMetaData("ssl_peer_certificate", pc.toString());
00811 
00812     if (pc.chain().isValid() && pc.chain().depth() > 1) {
00813        QString theChain;
00814        QPtrList<KSSLCertificate> chain = pc.chain().getChain();
00815        for (KSSLCertificate *c = chain.first(); c; c = chain.next()) {
00816           theChain += c->toString();
00817           theChain += "\n";
00818        }
00819        setMetaData("ssl_peer_chain", theChain);
00820     } else setMetaData("ssl_peer_chain", "");
00821 
00822    setMetaData("ssl_cert_state", QString::number(ksv));
00823 
00824    if (ksv == KSSLCertificate::Ok) {
00825       rc = 1;
00826       setMetaData("ssl_action", "accept");
00827    }
00828 
00829    kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl;
00830    if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
00831       // Since we're the parent, we need to teach the child.
00832       setMetaData("ssl_parent_ip", d->ip);
00833       setMetaData("ssl_parent_cert", pc.toString());
00834       //  - Read from cache and see if there is a policy for this
00835       KSSLCertificateCache::KSSLCertificatePolicy cp =
00836                                          d->cc->getPolicyByCertificate(pc);
00837 
00838       //  - validation code
00839       if (ksv != KSSLCertificate::Ok) {
00840          if (d->militantSSL) {
00841             return -1;
00842          }
00843 
00844          if (cp == KSSLCertificateCache::Unknown ||
00845              cp == KSSLCertificateCache::Ambiguous) {
00846             cp = KSSLCertificateCache::Prompt;
00847          } else {
00848             // A policy was already set so let's honor that.
00849             permacache = d->cc->isPermanent(pc);
00850          }
00851 
00852          if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
00853             cp = KSSLCertificateCache::Prompt;
00854 //            ksv = KSSLCertificate::Ok;
00855          }
00856 
00857          // Precondition: cp is one of Reject, Accept or Prompt
00858          switch (cp) {
00859          case KSSLCertificateCache::Accept:
00860            rc = 1;
00861            setMetaData("ssl_action", "accept");
00862           break;
00863          case KSSLCertificateCache::Reject:
00864            rc = -1;
00865            setMetaData("ssl_action", "reject");
00866           break;
00867          case KSSLCertificateCache::Prompt:
00868            {
00869              do {
00870                 if (ksv == KSSLCertificate::InvalidHost) {
00871                         QString msg = i18n("The IP address of the host %1 "
00872                                            "does not match the one the "
00873                                            "certificate was issued to.");
00874                    result = messageBox( WarningYesNoCancel,
00875                               msg.arg(ourHost),
00876                               i18n("Server Authentication"),
00877                               i18n("&Details"),
00878                               i18n("Co&ntinue") );
00879                 } else {
00880                    QString msg = i18n("The server certificate failed the "
00881                                       "authenticity test (%1).");
00882                    result = messageBox( WarningYesNoCancel,
00883                               msg.arg(ourHost),
00884                               i18n("Server Authentication"),
00885                               i18n("&Details"),
00886                               i18n("Co&ntinue") );
00887                 }
00888 
00889                 if (result == KMessageBox::Yes) {
00890                    if (!d->dcc) {
00891                       d->dcc = new DCOPClient;
00892                       d->dcc->attach();
00893                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
00894                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
00895                          QStringList() );
00896                       }
00897 
00898                    }
00899                    QByteArray data, ignore;
00900                    QCString ignoretype;
00901                    QDataStream arg(data, IO_WriteOnly);
00902                    arg << theurl << mOutgoingMetaData;
00903                         d->dcc->call("kio_uiserver", "UIServer",
00904                                 "showSSLInfoDialog(QString,KIO::MetaData)",
00905                                 data, ignoretype, ignore);
00906                 }
00907              } while (result == KMessageBox::Yes);
00908 
00909              if (result == KMessageBox::No) {
00910                 setMetaData("ssl_action", "accept");
00911                 rc = 1;
00912                 cp = KSSLCertificateCache::Accept;
00913                 doAddHost = true;
00914                    result = messageBox( WarningYesNo,
00915                                   i18n("Would you like to accept this "
00916                                        "certificate forever without "
00917                                        "being prompted?"),
00918                                   i18n("Server Authentication"),
00919                                          i18n("&Forever"),
00920                                          i18n("&Current Sessions Only"));
00921                     if (result == KMessageBox::Yes)
00922                         permacache = true;
00923                     else
00924                         permacache = false;
00925              } else {
00926                 setMetaData("ssl_action", "reject");
00927                 rc = -1;
00928                 cp = KSSLCertificateCache::Prompt;
00929              }
00930           break;
00931             }
00932          default:
00933           kdDebug(7029) << "TCPSlaveBase/SSL error in cert code."
00934                               << "Please report this to kfm-devel@kde.org."
00935                               << endl;
00936           break;
00937          }
00938       }
00939 
00940 
00941       //  - cache the results
00942       d->cc->addCertificate(pc, cp, permacache);
00943       if (doAddHost) d->cc->addHost(pc, ourHost);
00944     } else {    // Child frame
00945       //  - Read from cache and see if there is a policy for this
00946       KSSLCertificateCache::KSSLCertificatePolicy cp =
00947                                              d->cc->getPolicyByCertificate(pc);
00948       isChild = true;
00949 
00950       // Check the cert and IP to make sure they're the same
00951       // as the parent frame
00952       bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
00953                                pc.toString() == metaData("ssl_parent_cert"));
00954 
00955       if (ksv == KSSLCertificate::Ok) {
00956         if (certAndIPTheSame) {       // success
00957           rc = 1;
00958           setMetaData("ssl_action", "accept");
00959         } else {
00960           /*
00961           if (d->militantSSL) {
00962             return -1;
00963           }
00964           result = messageBox(WarningYesNo,
00965                               i18n("The certificate is valid but does not appear to have been assigned to this server.  Do you wish to continue loading?"),
00966                               i18n("Server Authentication"));
00967           if (result == KMessageBox::Yes) {     // success
00968             rc = 1;
00969             setMetaData("ssl_action", "accept");
00970           } else {    // fail
00971             rc = -1;
00972             setMetaData("ssl_action", "reject");
00973           }
00974           */
00975           setMetaData("ssl_action", "accept");
00976           rc = 1;   // Let's accept this now.  It's bad, but at least the user
00977                     // will see potential attacks in KDE3 with the pseudo-lock
00978                     // icon on the toolbar, and can investigate with the RMB
00979         }
00980       } else {
00981         if (d->militantSSL) {
00982           return -1;
00983         }
00984 
00985         if (cp == KSSLCertificateCache::Accept) {
00986            if (certAndIPTheSame) {    // success
00987              rc = 1;
00988              setMetaData("ssl_action", "accept");
00989            } else {   // fail
00990              result = messageBox(WarningYesNo,
00991                                  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?"),
00992                                  i18n("Server Authentication"));
00993              if (result == KMessageBox::Yes) {
00994                rc = 1;
00995                setMetaData("ssl_action", "accept");
00996                d->cc->addHost(pc, ourHost);
00997              } else {
00998                rc = -1;
00999                setMetaData("ssl_action", "reject");
01000              }
01001            }
01002         } else if (cp == KSSLCertificateCache::Reject) {      // fail
01003           messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE Control Center."),
01004                                   i18n("Server Authentication"));
01005           rc = -1;
01006           setMetaData("ssl_action", "reject");
01007         } else {
01008           do {
01009              QString msg = i18n("The server certificate failed the "
01010                                 "authenticity test (%1).");
01011              result = messageBox(WarningYesNoCancel,
01012                                  msg.arg(ourHost),
01013                                  i18n("Server Authentication"),
01014                                  i18n("&Details"),
01015                                  i18n("Co&nnect"));
01016                 if (result == KMessageBox::Yes) {
01017                    if (!d->dcc) {
01018                       d->dcc = new DCOPClient;
01019                       d->dcc->attach();
01020                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01021                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01022                          QStringList() );
01023                       }
01024                    }
01025                    QByteArray data, ignore;
01026                    QCString ignoretype;
01027                    QDataStream arg(data, IO_WriteOnly);
01028                    arg << theurl << mOutgoingMetaData;
01029                         d->dcc->call("kio_uiserver", "UIServer",
01030                                 "showSSLInfoDialog(QString,KIO::MetaData)",
01031                                 data, ignoretype, ignore);
01032                 }
01033           } while (result == KMessageBox::Yes);
01034 
01035           if (result == KMessageBox::No) {
01036              setMetaData("ssl_action", "accept");
01037              rc = 1;
01038              cp = KSSLCertificateCache::Accept;
01039              result = messageBox(WarningYesNo,
01040                                  i18n("Would you like to accept this "
01041                                       "certificate forever without "
01042                                       "being prompted?"),
01043                                  i18n("Server Authentication"),
01044                                  i18n("&Forever"),
01045                                  i18n("&Current Sessions Only"));
01046              permacache = (result == KMessageBox::Yes);
01047              d->cc->addCertificate(pc, cp, permacache);
01048              d->cc->addHost(pc, ourHost);
01049           } else {
01050              setMetaData("ssl_action", "reject");
01051              rc = -1;
01052              cp = KSSLCertificateCache::Prompt;
01053              d->cc->addCertificate(pc, cp, permacache);
01054           }
01055         }
01056       }
01057     }
01058 
01059 
01060    if (rc == -1) {
01061       return rc;
01062    }
01063 
01064    if (metaData("ssl_activate_warnings") == "TRUE") {
01065    //  - entering SSL
01066    if (!isChild && metaData("ssl_was_in_use") == "FALSE" &&
01067                                         d->kssl->settings()->warnOnEnter()) {
01068      int result;
01069      do {
01070                 result = messageBox(               i18n("You are about to "
01071                                                         "enter secure mode. "
01072                                                         "All transmissions "
01073                                                         "will be encrypted "
01074                                                         "unless otherwise "
01075                                                         "noted.\nThis means "
01076                                                         "that no third party "
01077                                                         "will be able to "
01078                                                         "easily observe your "
01079                                                         "data in transit."),
01080                                                    WarningYesNo,
01081                                                    i18n("Security Information"),
01082                                                    i18n("Display SSL "
01083                                                         "&Information"),
01084                                                    i18n("C&onnect"),
01085                                                    "WarnOnEnterSSLMode" );
01086       // Move this setting into KSSL instead
01087       KConfig *config = new KConfig("kioslaverc");
01088       config->setGroup("Notification Messages");
01089 
01090       if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) {
01091           config->deleteEntry("WarnOnEnterSSLMode");
01092           config->sync();
01093           d->kssl->settings()->setWarnOnEnter(false);
01094           d->kssl->settings()->save();
01095       }
01096       delete config;
01097 
01098       if ( result == KMessageBox::Yes )
01099       {
01100           if (!d->dcc) {
01101              d->dcc = new DCOPClient;
01102              d->dcc->attach();
01103              if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01104                 KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01105                 QStringList() );
01106              }
01107           }
01108           QByteArray data, ignore;
01109           QCString ignoretype;
01110           QDataStream arg(data, IO_WriteOnly);
01111           arg << theurl << mOutgoingMetaData;
01112           d->dcc->call("kio_uiserver", "UIServer",
01113                        "showSSLInfoDialog(QString,KIO::MetaData)",
01114                        data, ignoretype, ignore);
01115       }
01116       } while (result != KMessageBox::No);
01117    }
01118 
01119    }   // if ssl_activate_warnings
01120 
01121 
01122    kdDebug(7029) << "SSL connection information follows:" << endl
01123           << "+-----------------------------------------------" << endl
01124           << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
01125           << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
01126           << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
01127           << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
01128           << " of " << d->kssl->connectionInfo().getCipherBits()
01129           << " bits used." << endl
01130           << "| PEER:" << endl
01131           << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
01132           << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
01133           << "| Validation: " << (int)ksv << endl
01134           << "| Certificate matches IP: " << _IPmatchesCN << endl
01135           << "+-----------------------------------------------"
01136           << endl;
01137 
01138    // sendMetaData();  Do not call this function!!
01139    return rc;
01140 }
01141 
01142 
01143 bool TCPSlaveBase::isConnectionValid()
01144 {
01145     if ( m_iSock == -1 )
01146       return false;
01147 
01148     fd_set rdfs;
01149     FD_ZERO(&rdfs);
01150     FD_SET(m_iSock , &rdfs);
01151 
01152     struct timeval tv;
01153     tv.tv_usec = 0;
01154     tv.tv_sec = 0;
01155     int retval;
01156     do {
01157        retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
01158        if (wasKilled())
01159           return false; // Beam us out of here
01160     } while ((retval == -1) && (errno == EAGAIN));
01161     // retval == -1 ==> Error
01162     // retval ==  0 ==> Connection Idle
01163     // retval >=  1 ==> Connection Active
01164     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
01165     //              << retval << endl;
01166 
01167     if (retval == -1)
01168        return false;
01169 
01170     if (retval == 0)
01171        return true;
01172 
01173     // Connection is active, check if it has closed.
01174     char buffer[100];
01175     do {
01176        retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
01177 
01178     } while ((retval == -1) && (errno == EAGAIN));
01179     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
01180     //                 << retval << endl;
01181     if (retval <= 0)
01182        return false; // Error or connection closed.
01183 
01184     return true; // Connection still valid.
01185 }
01186 
01187 
01188 bool TCPSlaveBase::waitForResponse( int t )
01189 {
01190   fd_set rd;
01191   struct timeval timeout;
01192 
01193   if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
01194     if (d->kssl->pending() > 0)
01195         return true;
01196 
01197   FD_ZERO(&rd);
01198   FD_SET(m_iSock, &rd);
01199 
01200   timeout.tv_usec = 0;
01201   timeout.tv_sec = t;
01202   time_t startTime;
01203 
01204   int rc;
01205   int n = t;
01206 
01207 reSelect:
01208   startTime = time(NULL);
01209   rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
01210   if (wasKilled())
01211     return false; // We're dead.
01212 
01213   if (rc == -1)
01214     return false;
01215 
01216   if (FD_ISSET(m_iSock, &rd))
01217     return true;
01218 
01219   // Well it returned but it wasn't set.  Let's see if it
01220   // returned too early (perhaps from an errant signal) and
01221   // start over with the remaining time
01222   int timeDone = time(NULL) - startTime;
01223   if (timeDone < n)
01224   {
01225     n -= timeDone;
01226     timeout.tv_sec = n;
01227     goto reSelect;
01228   }
01229 
01230   return false; // Timed out!
01231 }
01232 
01233 int TCPSlaveBase::connectResult()
01234 {
01235     return d->status;
01236 }
01237 
01238 void TCPSlaveBase::setBlockConnection( bool b )
01239 {
01240     d->block = b;
01241 }
01242 
01243 void TCPSlaveBase::setConnectTimeout( int t )
01244 {
01245     d->timeout = t;
01246 }
01247 
01248 bool TCPSlaveBase::isSSLTunnelEnabled()
01249 {
01250     return d->useSSLTunneling;
01251 }
01252 
01253 void TCPSlaveBase::setEnableSSLTunnel( bool enable )
01254 {
01255     d->useSSLTunneling = enable;
01256 }
01257 
01258 void TCPSlaveBase::setRealHost( const QString& realHost )
01259 {
01260     d->realHost = realHost;
01261 }
01262 
01263 bool TCPSlaveBase::doSSLHandShake( bool sendError )
01264 {
01265     kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
01266     QString msgHost = d->host;
01267 
01268     d->kssl->reInitialize();
01269 
01270     if (hasMetaData("ssl_session_id")) {
01271         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
01272         if (s) {
01273             d->kssl->setSession(s);
01274             delete s;
01275     }    
01276     }
01277     certificatePrompt();
01278 
01279     if ( !d->realHost.isEmpty() )
01280     {
01281       msgHost = d->realHost;
01282     }
01283 
01284     kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
01285     d->kssl->setPeerHost(msgHost);
01286 
01287     d->status = d->kssl->connect(m_iSock);
01288     if (d->status < 0)
01289     {
01290         closeDescriptor();
01291         if ( sendError )
01292             error( ERR_COULD_NOT_CONNECT, msgHost);
01293         return false;
01294     }
01295 
01296     setMetaData("ssl_session_id", d->kssl->session()->toString());
01297     setMetaData("ssl_in_use", "TRUE");
01298 
01299     if (!d->kssl->reusingSession()) {
01300         int rc = verifyCertificate();
01301         if ( rc != 1 ) {
01302             d->status = -1;
01303             closeDescriptor();
01304             if ( sendError )
01305                 error( ERR_COULD_NOT_CONNECT, msgHost);
01306             return false;
01307         }
01308     }
01309 
01310     d->needSSLHandShake = false;
01311 
01312     d->savedMetaData = mOutgoingMetaData;
01313     return true;
01314 }
01315 
01316 
01317 bool TCPSlaveBase::userAborted() const
01318 {
01319    return d->userAborted;
01320 }
01321 
01322 void TCPSlaveBase::virtual_hook( int id, void* data )
01323 { SlaveBase::virtual_hook( id, data ); }
01324 
KDE Logo
This file is part of the documentation for kio Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:15:32 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003