kio Library API Documentation

tcpslavebase.cpp

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