kio Library API Documentation

tcpslavebase.cpp

00001 /*
00002  * $Id: tcpslavebase.cpp,v 1.139 2004/11/04 18:33:46 lunakl 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 #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 showprompt = !checkCachedAuthentication(ai);
00672      do {
00673         QString pass;
00674         QByteArray authdata, authval;
00675         QCString rettype;
00676         QDataStream qds(authdata, IO_WriteOnly);
00677         ai.prompt = i18n("Enter the certificate password:");
00678         ai.caption = i18n("SSL Certificate Password");
00679         ai.setModified(true);
00680         ai.username = certname;
00681         ai.keepPassword = true;
00682         if (showprompt) {
00683            qds << ai;
00684 
00685            if (!d->dcc) {
00686               d->dcc = new DCOPClient;
00687               d->dcc->attach();
00688               if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
00689                  KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
00690                                                          QStringList() );
00691              }
00692            }
00693 
00694            bool rc = d->dcc->call("kio_uiserver", "UIServer",
00695                                    "openPassDlg(KIO::AuthInfo)",
00696                                    authdata, rettype, authval);
00697            if (!rc) {
00698              break;
00699            }
00700            if (rettype != "QByteArray") {
00701              continue;
00702            }
00703 
00704            QDataStream qdret(authval, IO_ReadOnly);
00705            QByteArray authdecode;
00706            qdret >> authdecode;
00707            QDataStream qdtoo(authdecode, IO_ReadOnly);
00708            qdtoo >> ai;
00709            if (!ai.isModified()) {
00710              break;
00711            }
00712         }
00713         pass = ai.password;
00714         pkcs = KSSLCertificateHome::getCertificateByName(certname, pass);
00715 
00716         if (!pkcs) {
00717               int rc = messageBox(WarningYesNo, i18n("Unable to open the "
00718                                                      "certificate. Try a "
00719                                                      "new password?"),
00720                                                 i18n("SSL"));
00721               if (rc == KMessageBox::No) {
00722                 break;
00723               }
00724               showprompt = true;
00725         }
00726      } while (!pkcs);
00727      if (pkcs) {
00728        cacheAuthentication(ai);
00729      }
00730   }
00731 
00732    // If we could open the certificate, let's send it
00733    if (pkcs) {
00734       if (!d->kssl->setClientCertificate(pkcs)) {
00735             messageBox(Information, i18n("The procedure to set the "
00736                                          "client certificate for the session "
00737                                          "failed."), i18n("SSL"));
00738          delete pkcs;  // we don't need this anymore
00739          pkcs = 0L;
00740       } else {
00741          kdDebug(7029) << "Client SSL certificate is being used." << endl;
00742          setMetaData("ssl_using_client_cert", "TRUE");
00743          if (save) {
00744                 KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
00745                                                            true, false);
00746          }
00747       }
00748       d->pkcs = pkcs;
00749    }
00750 }
00751 
00752 
00753 
00754 bool TCPSlaveBase::usingTLS() const
00755 {
00756     return d->usingTLS;
00757 }
00758 
00759 // ### remove this for KDE4 (misses const):
00760 bool TCPSlaveBase::usingTLS()
00761 {
00762     return d->usingTLS;
00763 }
00764 
00765 
00766 //  Returns 0 for failed verification, -1 for rejected cert and 1 for ok
00767 int TCPSlaveBase::verifyCertificate()
00768 {
00769     int rc = 0;
00770     bool permacache = false;
00771     bool isChild = false;
00772     bool _IPmatchesCN = false;
00773     int result;
00774     bool doAddHost = false;
00775     QString ourHost;
00776 
00777     if (!d->realHost.isEmpty())
00778         ourHost = d->realHost;
00779     else ourHost = d->host;
00780 
00781     QString theurl = QString(m_sServiceName)+"://"+ourHost+":"+QString::number(m_iPort);
00782 
00783    if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE")
00784      d->militantSSL = false;
00785    else if (metaData("ssl_militant") == "TRUE")
00786      d->militantSSL = true;
00787 
00788     if (!d->cc) d->cc = new KSSLCertificateCache;
00789 
00790     KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate();
00791 
00792     KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer);
00793 
00794    _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
00795    if (!_IPmatchesCN && !d->militantSSL) {  // force this if the user wants it
00796       if (d->cc->getHostList(pc).contains(ourHost))
00797          _IPmatchesCN = true;
00798    }
00799 
00800    if (!_IPmatchesCN)
00801    {
00802       ksvl << KSSLCertificate::InvalidHost;
00803    }
00804 
00805    KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok;
00806    if (!ksvl.isEmpty())
00807       ksv = ksvl.first();
00808 
00809     /* Setting the various bits of meta-info that will be needed. */
00810     setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher());
00811     setMetaData("ssl_cipher_desc",
00812                             d->kssl->connectionInfo().getCipherDescription());
00813     setMetaData("ssl_cipher_version",
00814                                 d->kssl->connectionInfo().getCipherVersion());
00815     setMetaData("ssl_cipher_used_bits",
00816               QString::number(d->kssl->connectionInfo().getCipherUsedBits()));
00817     setMetaData("ssl_cipher_bits",
00818                   QString::number(d->kssl->connectionInfo().getCipherBits()));
00819     setMetaData("ssl_peer_ip", d->ip);
00820     
00821     QString errorStr;
00822     for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
00823         it != ksvl.end(); ++it)
00824     {
00825        errorStr += QString::number(*it)+":";
00826     }
00827     setMetaData("ssl_cert_errors", errorStr);
00828     setMetaData("ssl_peer_certificate", pc.toString());
00829 
00830     if (pc.chain().isValid() && pc.chain().depth() > 1) {
00831        QString theChain;
00832        QPtrList<KSSLCertificate> chain = pc.chain().getChain();
00833        for (KSSLCertificate *c = chain.first(); c; c = chain.next()) {
00834           theChain += c->toString();
00835           theChain += "\n";
00836        }
00837        setMetaData("ssl_peer_chain", theChain);
00838     } else setMetaData("ssl_peer_chain", "");
00839 
00840    setMetaData("ssl_cert_state", QString::number(ksv));
00841 
00842    if (ksv == KSSLCertificate::Ok) {
00843       rc = 1;
00844       setMetaData("ssl_action", "accept");
00845    }
00846 
00847    kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl;
00848    if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
00849       // Since we're the parent, we need to teach the child.
00850       setMetaData("ssl_parent_ip", d->ip);
00851       setMetaData("ssl_parent_cert", pc.toString());
00852       //  - Read from cache and see if there is a policy for this
00853       KSSLCertificateCache::KSSLCertificatePolicy cp =
00854                                          d->cc->getPolicyByCertificate(pc);
00855 
00856       //  - validation code
00857       if (ksv != KSSLCertificate::Ok) {
00858          if (d->militantSSL) {
00859             return -1;
00860          }
00861 
00862          if (cp == KSSLCertificateCache::Unknown ||
00863              cp == KSSLCertificateCache::Ambiguous) {
00864             cp = KSSLCertificateCache::Prompt;
00865          } else {
00866             // A policy was already set so let's honor that.
00867             permacache = d->cc->isPermanent(pc);
00868          }
00869 
00870          if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
00871             cp = KSSLCertificateCache::Prompt;
00872 //            ksv = KSSLCertificate::Ok;
00873          }
00874 
00875          // Precondition: cp is one of Reject, Accept or Prompt
00876          switch (cp) {
00877          case KSSLCertificateCache::Accept:
00878            rc = 1;
00879            setMetaData("ssl_action", "accept");
00880           break;
00881          case KSSLCertificateCache::Reject:
00882            rc = -1;
00883            setMetaData("ssl_action", "reject");
00884           break;
00885          case KSSLCertificateCache::Prompt:
00886            {
00887              do {
00888                 if (ksv == KSSLCertificate::InvalidHost) {
00889                         QString msg = i18n("The IP address of the host %1 "
00890                                            "does not match the one the "
00891                                            "certificate was issued to.");
00892                    result = messageBox( WarningYesNoCancel,
00893                               msg.arg(ourHost),
00894                               i18n("Server Authentication"),
00895                               i18n("&Details"),
00896                               i18n("Co&ntinue") );
00897                 } else {
00898                    QString msg = i18n("The server certificate failed the "
00899                                       "authenticity test (%1).");
00900                    result = messageBox( WarningYesNoCancel,
00901                               msg.arg(ourHost),
00902                               i18n("Server Authentication"),
00903                               i18n("&Details"),
00904                               i18n("Co&ntinue") );
00905                 }
00906 
00907                 if (result == KMessageBox::Yes) {
00908                    if (!d->dcc) {
00909                       d->dcc = new DCOPClient;
00910                       d->dcc->attach();
00911                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
00912                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
00913                          QStringList() );
00914                       }
00915 
00916                    }
00917                    QByteArray data, ignore;
00918                    QCString ignoretype;
00919                    QDataStream arg(data, IO_WriteOnly);
00920                    arg << theurl << mOutgoingMetaData;
00921                    arg << metaData("window-id").toInt();
00922                         d->dcc->call("kio_uiserver", "UIServer",
00923                                 "showSSLInfoDialog(QString,KIO::MetaData,int)",
00924                                 data, ignoretype, ignore);
00925                 }
00926              } while (result == KMessageBox::Yes);
00927 
00928              if (result == KMessageBox::No) {
00929                 setMetaData("ssl_action", "accept");
00930                 rc = 1;
00931                 cp = KSSLCertificateCache::Accept;
00932                 doAddHost = true;
00933                    result = messageBox( WarningYesNo,
00934                                   i18n("Would you like to accept this "
00935                                        "certificate forever without "
00936                                        "being prompted?"),
00937                                   i18n("Server Authentication"),
00938                                          i18n("&Forever"),
00939                                          i18n("&Current Sessions Only"));
00940                     if (result == KMessageBox::Yes)
00941                         permacache = true;
00942                     else
00943                         permacache = false;
00944              } else {
00945                 setMetaData("ssl_action", "reject");
00946                 rc = -1;
00947                 cp = KSSLCertificateCache::Prompt;
00948              }
00949           break;
00950             }
00951          default:
00952           kdDebug(7029) << "TCPSlaveBase/SSL error in cert code."
00953                               << "Please report this to kfm-devel@kde.org."
00954                               << endl;
00955           break;
00956          }
00957       }
00958 
00959 
00960       //  - cache the results
00961       d->cc->addCertificate(pc, cp, permacache);
00962       if (doAddHost) d->cc->addHost(pc, ourHost);
00963     } else {    // Child frame
00964       //  - Read from cache and see if there is a policy for this
00965       KSSLCertificateCache::KSSLCertificatePolicy cp =
00966                                              d->cc->getPolicyByCertificate(pc);
00967       isChild = true;
00968 
00969       // Check the cert and IP to make sure they're the same
00970       // as the parent frame
00971       bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
00972                                pc.toString() == metaData("ssl_parent_cert"));
00973 
00974       if (ksv == KSSLCertificate::Ok) {
00975         if (certAndIPTheSame) {       // success
00976           rc = 1;
00977           setMetaData("ssl_action", "accept");
00978         } else {
00979           /*
00980           if (d->militantSSL) {
00981             return -1;
00982           }
00983           result = messageBox(WarningYesNo,
00984                               i18n("The certificate is valid but does not appear to have been assigned to this server.  Do you wish to continue loading?"),
00985                               i18n("Server Authentication"));
00986           if (result == KMessageBox::Yes) {     // success
00987             rc = 1;
00988             setMetaData("ssl_action", "accept");
00989           } else {    // fail
00990             rc = -1;
00991             setMetaData("ssl_action", "reject");
00992           }
00993           */
00994           setMetaData("ssl_action", "accept");
00995           rc = 1;   // Let's accept this now.  It's bad, but at least the user
00996                     // will see potential attacks in KDE3 with the pseudo-lock
00997                     // icon on the toolbar, and can investigate with the RMB
00998         }
00999       } else {
01000         if (d->militantSSL) {
01001           return -1;
01002         }
01003 
01004         if (cp == KSSLCertificateCache::Accept) {
01005            if (certAndIPTheSame) {    // success
01006              rc = 1;
01007              setMetaData("ssl_action", "accept");
01008            } else {   // fail
01009              result = messageBox(WarningYesNo,
01010                                  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?"),
01011                                  i18n("Server Authentication"));
01012              if (result == KMessageBox::Yes) {
01013                rc = 1;
01014                setMetaData("ssl_action", "accept");
01015                d->cc->addHost(pc, ourHost);
01016              } else {
01017                rc = -1;
01018                setMetaData("ssl_action", "reject");
01019              }
01020            }
01021         } else if (cp == KSSLCertificateCache::Reject) {      // fail
01022           messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE Control Center."),
01023                                   i18n("Server Authentication"));
01024           rc = -1;
01025           setMetaData("ssl_action", "reject");
01026         } else {
01027           do {
01028              QString msg = i18n("The server certificate failed the "
01029                                 "authenticity test (%1).");
01030              result = messageBox(WarningYesNoCancel,
01031                                  msg.arg(ourHost),
01032                                  i18n("Server Authentication"),
01033                                  i18n("&Details"),
01034                                  i18n("Co&nnect"));
01035                 if (result == KMessageBox::Yes) {
01036                    if (!d->dcc) {
01037                       d->dcc = new DCOPClient;
01038                       d->dcc->attach();
01039                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01040                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01041                          QStringList() );
01042                       }
01043                    }
01044                    QByteArray data, ignore;
01045                    QCString ignoretype;
01046                    QDataStream arg(data, IO_WriteOnly);
01047                    arg << theurl << mOutgoingMetaData;
01048                    arg << metaData("window-id").toInt();
01049                         d->dcc->call("kio_uiserver", "UIServer",
01050                                 "showSSLInfoDialog(QString,KIO::MetaData,int)",
01051                                 data, ignoretype, ignore);
01052                 }
01053           } while (result == KMessageBox::Yes);
01054 
01055           if (result == KMessageBox::No) {
01056              setMetaData("ssl_action", "accept");
01057              rc = 1;
01058              cp = KSSLCertificateCache::Accept;
01059              result = messageBox(WarningYesNo,
01060                                  i18n("Would you like to accept this "
01061                                       "certificate forever without "
01062                                       "being prompted?"),
01063                                  i18n("Server Authentication"),
01064                                  i18n("&Forever"),
01065                                  i18n("&Current Sessions Only"));
01066              permacache = (result == KMessageBox::Yes);
01067              d->cc->addCertificate(pc, cp, permacache);
01068              d->cc->addHost(pc, ourHost);
01069           } else {
01070              setMetaData("ssl_action", "reject");
01071              rc = -1;
01072              cp = KSSLCertificateCache::Prompt;
01073              d->cc->addCertificate(pc, cp, permacache);
01074           }
01075         }
01076       }
01077     }
01078 
01079 
01080    if (rc == -1) {
01081       return rc;
01082    }
01083 
01084    if (metaData("ssl_activate_warnings") == "TRUE") {
01085    //  - entering SSL
01086    if (!isChild && metaData("ssl_was_in_use") == "FALSE" &&
01087                                         d->kssl->settings()->warnOnEnter()) {
01088      int result;
01089      do {
01090                 result = messageBox(               i18n("You are about to "
01091                                                         "enter secure mode. "
01092                                                         "All transmissions "
01093                                                         "will be encrypted "
01094                                                         "unless otherwise "
01095                                                         "noted.\nThis means "
01096                                                         "that no third party "
01097                                                         "will be able to "
01098                                                         "easily observe your "
01099                                                         "data in transit."),
01100                                                    WarningYesNo,
01101                                                    i18n("Security Information"),
01102                                                    i18n("Display SSL "
01103                                                         "&Information"),
01104                                                    i18n("C&onnect"),
01105                                                    "WarnOnEnterSSLMode" );
01106       // Move this setting into KSSL instead
01107       KConfig *config = new KConfig("kioslaverc");
01108       config->setGroup("Notification Messages");
01109 
01110       if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) {
01111           config->deleteEntry("WarnOnEnterSSLMode");
01112           config->sync();
01113           d->kssl->settings()->setWarnOnEnter(false);
01114           d->kssl->settings()->save();
01115       }
01116       delete config;
01117 
01118       if ( result == KMessageBox::Yes )
01119       {
01120           if (!d->dcc) {
01121              d->dcc = new DCOPClient;
01122              d->dcc->attach();
01123              if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01124                 KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01125                 QStringList() );
01126              }
01127           }
01128           QByteArray data, ignore;
01129           QCString ignoretype;
01130           QDataStream arg(data, IO_WriteOnly);
01131           arg << theurl << mOutgoingMetaData;
01132           arg << metaData("window-id").toInt();
01133           d->dcc->call("kio_uiserver", "UIServer",
01134                        "showSSLInfoDialog(QString,KIO::MetaData,int)",
01135                        data, ignoretype, ignore);
01136       }
01137       } while (result != KMessageBox::No);
01138    }
01139 
01140    }   // if ssl_activate_warnings
01141 
01142 
01143    kdDebug(7029) << "SSL connection information follows:" << endl
01144           << "+-----------------------------------------------" << endl
01145           << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
01146           << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
01147           << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
01148           << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
01149           << " of " << d->kssl->connectionInfo().getCipherBits()
01150           << " bits used." << endl
01151           << "| PEER:" << endl
01152           << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
01153           << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
01154           << "| Validation: " << (int)ksv << endl
01155           << "| Certificate matches IP: " << _IPmatchesCN << endl
01156           << "+-----------------------------------------------"
01157           << endl;
01158 
01159    // sendMetaData();  Do not call this function!!
01160    return rc;
01161 }
01162 
01163 
01164 bool TCPSlaveBase::isConnectionValid()
01165 {
01166     if ( m_iSock == -1 )
01167       return false;
01168 
01169     fd_set rdfs;
01170     FD_ZERO(&rdfs);
01171     FD_SET(m_iSock , &rdfs);
01172 
01173     struct timeval tv;
01174     tv.tv_usec = 0;
01175     tv.tv_sec = 0;
01176     int retval;
01177 #ifdef Q_OS_UNIX
01178     do {
01179        retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
01180        if (wasKilled())
01181           return false; // Beam us out of here
01182     } while ((retval == -1) && (errno == EAGAIN));
01183 #else
01184     retval = -1;
01185 #endif
01186     // retval == -1 ==> Error
01187     // retval ==  0 ==> Connection Idle
01188     // retval >=  1 ==> Connection Active
01189     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
01190     //              << retval << endl;
01191 
01192     if (retval == -1)
01193        return false;
01194 
01195     if (retval == 0)
01196        return true;
01197 
01198     // Connection is active, check if it has closed.
01199     char buffer[100];
01200 #ifdef Q_OS_UNIX
01201     do {
01202        retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
01203 
01204     } while ((retval == -1) && (errno == EAGAIN));
01205 #else
01206     retval = -1;
01207 #endif
01208     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
01209     //                 << retval << endl;
01210     if (retval <= 0)
01211        return false; // Error or connection closed.
01212 
01213     return true; // Connection still valid.
01214 }
01215 
01216 
01217 bool TCPSlaveBase::waitForResponse( int t )
01218 {
01219   fd_set rd;
01220   struct timeval timeout;
01221 
01222   if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
01223     if (d->kssl->pending() > 0)
01224         return true;
01225 
01226   FD_ZERO(&rd);
01227   FD_SET(m_iSock, &rd);
01228 
01229   timeout.tv_usec = 0;
01230   timeout.tv_sec = t;
01231   time_t startTime;
01232 
01233   int rc;
01234   int n = t;
01235 
01236 reSelect:
01237   startTime = time(NULL);
01238 #ifdef Q_OS_UNIX
01239   rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
01240 #else
01241   rc = -1;
01242 #endif
01243   if (wasKilled())
01244     return false; // We're dead.
01245 
01246   if (rc == -1)
01247     return false;
01248 
01249   if (FD_ISSET(m_iSock, &rd))
01250     return true;
01251 
01252   // Well it returned but it wasn't set.  Let's see if it
01253   // returned too early (perhaps from an errant signal) and
01254   // start over with the remaining time
01255   int timeDone = time(NULL) - startTime;
01256   if (timeDone < n)
01257   {
01258     n -= timeDone;
01259     timeout.tv_sec = n;
01260     goto reSelect;
01261   }
01262 
01263   return false; // Timed out!
01264 }
01265 
01266 int TCPSlaveBase::connectResult()
01267 {
01268     return d->status;
01269 }
01270 
01271 void TCPSlaveBase::setBlockConnection( bool b )
01272 {
01273     d->block = b;
01274 }
01275 
01276 void TCPSlaveBase::setConnectTimeout( int t )
01277 {
01278     d->timeout = t;
01279 }
01280 
01281 bool TCPSlaveBase::isSSLTunnelEnabled()
01282 {
01283     return d->useSSLTunneling;
01284 }
01285 
01286 void TCPSlaveBase::setEnableSSLTunnel( bool enable )
01287 {
01288     d->useSSLTunneling = enable;
01289 }
01290 
01291 void TCPSlaveBase::setRealHost( const QString& realHost )
01292 {
01293     d->realHost = realHost;
01294 }
01295 
01296 bool TCPSlaveBase::doSSLHandShake( bool sendError )
01297 {
01298     kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
01299     QString msgHost = d->host;
01300 
01301     d->kssl->reInitialize();
01302 
01303     if (hasMetaData("ssl_session_id")) {
01304         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
01305         if (s) {
01306             d->kssl->setSession(s);
01307             delete s;
01308     }    
01309     }
01310     certificatePrompt();
01311 
01312     if ( !d->realHost.isEmpty() )
01313     {
01314       msgHost = d->realHost;
01315     }
01316 
01317     kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
01318     d->kssl->setPeerHost(msgHost);
01319 
01320     d->status = d->kssl->connect(m_iSock);
01321     if (d->status < 0)
01322     {
01323         closeDescriptor();
01324         if ( sendError )
01325             error( ERR_COULD_NOT_CONNECT, msgHost);
01326         return false;
01327     }
01328 
01329     setMetaData("ssl_session_id", d->kssl->session()->toString());
01330     setMetaData("ssl_in_use", "TRUE");
01331 
01332     if (!d->kssl->reusingSession()) {
01333         int rc = verifyCertificate();
01334         if ( rc != 1 ) {
01335             d->status = -1;
01336             closeDescriptor();
01337             if ( sendError )
01338                 error( ERR_COULD_NOT_CONNECT, msgHost);
01339             return false;
01340         }
01341     }
01342 
01343     d->needSSLHandShake = false;
01344 
01345     d->savedMetaData = mOutgoingMetaData;
01346     return true;
01347 }
01348 
01349 
01350 bool TCPSlaveBase::userAborted() const
01351 {
01352    return d->userAborted;
01353 }
01354 
01355 void TCPSlaveBase::virtual_hook( int id, void* data )
01356 { SlaveBase::virtual_hook( id, data ); }
01357 
KDE Logo
This file is part of the documentation for kio Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 21 13:14:29 2006 by doxygen 1.4.0 written by Dimitri van Heesch, © 1997-2003