• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kio

tcpslavebase.cpp

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

kio

Skip menu "kio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal