tcpslavebase.cpp

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       bool dialogBoxStatus = false;
01096       if( config->hasKey("WarnOnEnterSSLMode"))
01097         dialogBoxStatus = true;
01098       bool keyStatus = config->readBoolEntry("WarnOnEnterSSLMode", true);
01099       dialogBoxStatus = dialogBoxStatus && keyStatus;
01100       if (!keyStatus) {
01101           config->deleteEntry("WarnOnEnterSSLMode");
01102           config->sync();
01103           d->kssl->settings()->setWarnOnEnter(false);
01104           d->kssl->settings()->save();
01105       }
01106       delete config;
01107 
01108       if ( result == KMessageBox::Yes )
01109       {
01110           if (!d->dcc) {
01111              d->dcc = new DCOPClient;
01112              d->dcc->attach();
01113              if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01114                 KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01115                 QStringList() );
01116              }
01117           }
01118           QByteArray data, ignore;
01119           QCString ignoretype;
01120           QDataStream arg(data, IO_WriteOnly);
01121           arg << theurl << mOutgoingMetaData;
01122           arg << metaData("window-id").toInt();
01123           d->dcc->call("kio_uiserver", "UIServer",
01124                        "showSSLInfoDialog(QString,KIO::MetaData,int)",
01125                        data, ignoretype, ignore);
01126       }
01127       //Laurent: If we disable message box we can't click on KMessageBox::No
01128       if(dialogBoxStatus)
01129           break;
01130       } while (result != KMessageBox::No);
01131    }
01132 
01133    }   // if ssl_activate_warnings
01134 
01135 
01136    kdDebug(7029) << "SSL connection information follows:" << endl
01137           << "+-----------------------------------------------" << endl
01138           << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
01139           << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
01140           << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
01141           << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
01142           << " of " << d->kssl->connectionInfo().getCipherBits()
01143           << " bits used." << endl
01144           << "| PEER:" << endl
01145           << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
01146           << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
01147           << "| Validation: " << (int)ksv << endl
01148           << "| Certificate matches IP: " << _IPmatchesCN << endl
01149           << "+-----------------------------------------------"
01150           << endl;
01151 
01152    // sendMetaData();  Do not call this function!!
01153    return rc;
01154 }
01155 
01156 
01157 bool TCPSlaveBase::isConnectionValid()
01158 {
01159     if ( m_iSock == -1 )
01160       return false;
01161 
01162     fd_set rdfs;
01163     FD_ZERO(&rdfs);
01164     FD_SET(m_iSock , &rdfs);
01165 
01166     struct timeval tv;
01167     tv.tv_usec = 0;
01168     tv.tv_sec = 0;
01169     int retval;
01170 #ifdef Q_OS_UNIX
01171     do {
01172        retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
01173        if (wasKilled())
01174           return false; // Beam us out of here
01175     } while ((retval == -1) && (errno == EAGAIN));
01176 #else
01177     retval = -1;
01178 #endif
01179     // retval == -1 ==> Error
01180     // retval ==  0 ==> Connection Idle
01181     // retval >=  1 ==> Connection Active
01182     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
01183     //              << retval << endl;
01184 
01185     if (retval == -1)
01186        return false;
01187 
01188     if (retval == 0)
01189        return true;
01190 
01191     // Connection is active, check if it has closed.
01192     char buffer[100];
01193 #ifdef Q_OS_UNIX
01194     do {
01195        retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
01196 
01197     } while ((retval == -1) && (errno == EAGAIN));
01198 #else
01199     retval = -1;
01200 #endif
01201     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
01202     //                 << retval << endl;
01203     if (retval <= 0)
01204        return false; // Error or connection closed.
01205 
01206     return true; // Connection still valid.
01207 }
01208 
01209 
01210 bool TCPSlaveBase::waitForResponse( int t )
01211 {
01212   fd_set rd;
01213   struct timeval timeout;
01214 
01215   if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
01216     if (d->kssl->pending() > 0)
01217         return true;
01218 
01219   FD_ZERO(&rd);
01220   FD_SET(m_iSock, &rd);
01221 
01222   timeout.tv_usec = 0;
01223   timeout.tv_sec = t;
01224   time_t startTime;
01225 
01226   int rc;
01227   int n = t;
01228 
01229 reSelect:
01230   startTime = time(NULL);
01231 #ifdef Q_OS_UNIX
01232   rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
01233 #else
01234   rc = -1;
01235 #endif
01236   if (wasKilled())
01237     return false; // We're dead.
01238 
01239   if (rc == -1)
01240     return false;
01241 
01242   if (FD_ISSET(m_iSock, &rd))
01243     return true;
01244 
01245   // Well it returned but it wasn't set.  Let's see if it
01246   // returned too early (perhaps from an errant signal) and
01247   // start over with the remaining time
01248   int timeDone = time(NULL) - startTime;
01249   if (timeDone < n)
01250   {
01251     n -= timeDone;
01252     timeout.tv_sec = n;
01253     goto reSelect;
01254   }
01255 
01256   return false; // Timed out!
01257 }
01258 
01259 int TCPSlaveBase::connectResult()
01260 {
01261     return d->status;
01262 }
01263 
01264 void TCPSlaveBase::setBlockConnection( bool b )
01265 {
01266     d->block = b;
01267 }
01268 
01269 void TCPSlaveBase::setConnectTimeout( int t )
01270 {
01271     d->timeout = t;
01272 }
01273 
01274 bool TCPSlaveBase::isSSLTunnelEnabled()
01275 {
01276     return d->useSSLTunneling;
01277 }
01278 
01279 void TCPSlaveBase::setEnableSSLTunnel( bool enable )
01280 {
01281     d->useSSLTunneling = enable;
01282 }
01283 
01284 void TCPSlaveBase::setRealHost( const QString& realHost )
01285 {
01286     d->realHost = realHost;
01287 }
01288 
01289 bool TCPSlaveBase::doSSLHandShake( bool sendError )
01290 {
01291     kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
01292     QString msgHost = d->host;
01293 
01294     d->kssl->reInitialize();
01295 
01296     if (hasMetaData("ssl_session_id")) {
01297         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
01298         if (s) {
01299             d->kssl->setSession(s);
01300             delete s;
01301     }    
01302     }
01303     certificatePrompt();
01304 
01305     if ( !d->realHost.isEmpty() )
01306     {
01307       msgHost = d->realHost;
01308     }
01309 
01310     kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
01311     d->kssl->setPeerHost(msgHost);
01312 
01313     d->status = d->kssl->connect(m_iSock);
01314     if (d->status < 0)
01315     {
01316         closeDescriptor();
01317         if ( sendError )
01318             error( ERR_COULD_NOT_CONNECT, msgHost);
01319         return false;
01320     }
01321 
01322     setMetaData("ssl_session_id", d->kssl->session()->toString());
01323     setMetaData("ssl_in_use", "TRUE");
01324 
01325     if (!d->kssl->reusingSession()) {
01326         int rc = verifyCertificate();
01327         if ( rc != 1 ) {
01328             d->status = -1;
01329             closeDescriptor();
01330             if ( sendError )
01331                 error( ERR_COULD_NOT_CONNECT, msgHost);
01332             return false;
01333         }
01334     }
01335 
01336     d->needSSLHandShake = false;
01337 
01338     d->savedMetaData = mOutgoingMetaData;
01339     return true;
01340 }
01341 
01342 
01343 bool TCPSlaveBase::userAborted() const
01344 {
01345    return d->userAborted;
01346 }
01347 
01348 void TCPSlaveBase::virtual_hook( int id, void* data )
01349 { SlaveBase::virtual_hook( id, data ); }
01350 
KDE Home | KDE Accessibility Home | Description of Access Keys