tcpslavebase.cpp

00001 /*
00002  * $Id: tcpslavebase.cpp 529518 2006-04-13 16:36:07Z mueller $
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        for (KSSLCertificate *c = chain.first(); c; c = chain.next()) {
00819           theChain += c->toString();
00820           theChain += "\n";
00821        }
00822        setMetaData("ssl_peer_chain", theChain);
00823     } else setMetaData("ssl_peer_chain", "");
00824 
00825    setMetaData("ssl_cert_state", QString::number(ksv));
00826 
00827    if (ksv == KSSLCertificate::Ok) {
00828       rc = 1;
00829       setMetaData("ssl_action", "accept");
00830    }
00831 
00832    kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl;
00833    if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
00834       // Since we're the parent, we need to teach the child.
00835       setMetaData("ssl_parent_ip", d->ip);
00836       setMetaData("ssl_parent_cert", pc.toString());
00837       //  - Read from cache and see if there is a policy for this
00838       KSSLCertificateCache::KSSLCertificatePolicy cp =
00839                                          d->cc->getPolicyByCertificate(pc);
00840 
00841       //  - validation code
00842       if (ksv != KSSLCertificate::Ok) {
00843          if (d->militantSSL) {
00844             return -1;
00845          }
00846 
00847          if (cp == KSSLCertificateCache::Unknown ||
00848              cp == KSSLCertificateCache::Ambiguous) {
00849             cp = KSSLCertificateCache::Prompt;
00850          } else {
00851             // A policy was already set so let's honor that.
00852             permacache = d->cc->isPermanent(pc);
00853          }
00854 
00855          if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
00856             cp = KSSLCertificateCache::Prompt;
00857 //            ksv = KSSLCertificate::Ok;
00858          }
00859 
00860          // Precondition: cp is one of Reject, Accept or Prompt
00861          switch (cp) {
00862          case KSSLCertificateCache::Accept:
00863            rc = 1;
00864            setMetaData("ssl_action", "accept");
00865           break;
00866          case KSSLCertificateCache::Reject:
00867            rc = -1;
00868            setMetaData("ssl_action", "reject");
00869           break;
00870          case KSSLCertificateCache::Prompt:
00871            {
00872              do {
00873                 if (ksv == KSSLCertificate::InvalidHost) {
00874                         QString msg = i18n("The IP address of the host %1 "
00875                                            "does not match the one the "
00876                                            "certificate was issued to.");
00877                    result = messageBox( WarningYesNoCancel,
00878                               msg.arg(ourHost),
00879                               i18n("Server Authentication"),
00880                               i18n("&Details"),
00881                               i18n("Co&ntinue") );
00882                 } else {
00883                    QString msg = i18n("The server certificate failed the "
00884                                       "authenticity test (%1).");
00885                    result = messageBox( WarningYesNoCancel,
00886                               msg.arg(ourHost),
00887                               i18n("Server Authentication"),
00888                               i18n("&Details"),
00889                               i18n("Co&ntinue") );
00890                 }
00891 
00892                 if (result == KMessageBox::Yes) {
00893                    if (!d->dcc) {
00894                       d->dcc = new DCOPClient;
00895                       d->dcc->attach();
00896                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
00897                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
00898                          QStringList() );
00899                       }
00900 
00901                    }
00902                    QByteArray data, ignore;
00903                    QCString ignoretype;
00904                    QDataStream arg(data, IO_WriteOnly);
00905                    arg << theurl << mOutgoingMetaData;
00906                    arg << metaData("window-id").toInt();
00907                         d->dcc->call("kio_uiserver", "UIServer",
00908                                 "showSSLInfoDialog(QString,KIO::MetaData,int)",
00909                                 data, ignoretype, ignore);
00910                 }
00911              } while (result == KMessageBox::Yes);
00912 
00913              if (result == KMessageBox::No) {
00914                 setMetaData("ssl_action", "accept");
00915                 rc = 1;
00916                 cp = KSSLCertificateCache::Accept;
00917                 doAddHost = true;
00918                    result = messageBox( WarningYesNo,
00919                                   i18n("Would you like to accept this "
00920                                        "certificate forever without "
00921                                        "being prompted?"),
00922                                   i18n("Server Authentication"),
00923                                          i18n("&Forever"),
00924                                          i18n("&Current Sessions Only"));
00925                     if (result == KMessageBox::Yes)
00926                         permacache = true;
00927                     else
00928                         permacache = false;
00929              } else {
00930                 setMetaData("ssl_action", "reject");
00931                 rc = -1;
00932                 cp = KSSLCertificateCache::Prompt;
00933              }
00934           break;
00935             }
00936          default:
00937           kdDebug(7029) << "TCPSlaveBase/SSL error in cert code."
00938                               << "Please report this to kfm-devel@kde.org."
00939                               << endl;
00940           break;
00941          }
00942       }
00943 
00944 
00945       //  - cache the results
00946       d->cc->addCertificate(pc, cp, permacache);
00947       if (doAddHost) d->cc->addHost(pc, ourHost);
00948     } else {    // Child frame
00949       //  - Read from cache and see if there is a policy for this
00950       KSSLCertificateCache::KSSLCertificatePolicy cp =
00951                                              d->cc->getPolicyByCertificate(pc);
00952       isChild = true;
00953 
00954       // Check the cert and IP to make sure they're the same
00955       // as the parent frame
00956       bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
00957                                pc.toString() == metaData("ssl_parent_cert"));
00958 
00959       if (ksv == KSSLCertificate::Ok) {
00960         if (certAndIPTheSame) {       // success
00961           rc = 1;
00962           setMetaData("ssl_action", "accept");
00963         } else {
00964           /*
00965           if (d->militantSSL) {
00966             return -1;
00967           }
00968           result = messageBox(WarningYesNo,
00969                               i18n("The certificate is valid but does not appear to have been assigned to this server.  Do you wish to continue loading?"),
00970                               i18n("Server Authentication"));
00971           if (result == KMessageBox::Yes) {     // success
00972             rc = 1;
00973             setMetaData("ssl_action", "accept");
00974           } else {    // fail
00975             rc = -1;
00976             setMetaData("ssl_action", "reject");
00977           }
00978           */
00979           setMetaData("ssl_action", "accept");
00980           rc = 1;   // Let's accept this now.  It's bad, but at least the user
00981                     // will see potential attacks in KDE3 with the pseudo-lock
00982                     // icon on the toolbar, and can investigate with the RMB
00983         }
00984       } else {
00985         if (d->militantSSL) {
00986           return -1;
00987         }
00988 
00989         if (cp == KSSLCertificateCache::Accept) {
00990            if (certAndIPTheSame) {    // success
00991              rc = 1;
00992              setMetaData("ssl_action", "accept");
00993            } else {   // fail
00994              result = messageBox(WarningYesNo,
00995                                  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?"),
00996                                  i18n("Server Authentication"));
00997              if (result == KMessageBox::Yes) {
00998                rc = 1;
00999                setMetaData("ssl_action", "accept");
01000                d->cc->addHost(pc, ourHost);
01001              } else {
01002                rc = -1;
01003                setMetaData("ssl_action", "reject");
01004              }
01005            }
01006         } else if (cp == KSSLCertificateCache::Reject) {      // fail
01007           messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE Control Center."),
01008                                   i18n("Server Authentication"));
01009           rc = -1;
01010           setMetaData("ssl_action", "reject");
01011         } else {
01012           do {
01013              QString msg = i18n("The server certificate failed the "
01014                                 "authenticity test (%1).");
01015              result = messageBox(WarningYesNoCancel,
01016                                  msg.arg(ourHost),
01017                                  i18n("Server Authentication"),
01018                                  i18n("&Details"),
01019                                  i18n("Co&nnect"));
01020                 if (result == KMessageBox::Yes) {
01021                    if (!d->dcc) {
01022                       d->dcc = new DCOPClient;
01023                       d->dcc->attach();
01024                       if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01025                          KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01026                          QStringList() );
01027                       }
01028                    }
01029                    QByteArray data, ignore;
01030                    QCString ignoretype;
01031                    QDataStream arg(data, IO_WriteOnly);
01032                    arg << theurl << mOutgoingMetaData;
01033                    arg << metaData("window-id").toInt();
01034                         d->dcc->call("kio_uiserver", "UIServer",
01035                                 "showSSLInfoDialog(QString,KIO::MetaData,int)",
01036                                 data, ignoretype, ignore);
01037                 }
01038           } while (result == KMessageBox::Yes);
01039 
01040           if (result == KMessageBox::No) {
01041              setMetaData("ssl_action", "accept");
01042              rc = 1;
01043              cp = KSSLCertificateCache::Accept;
01044              result = messageBox(WarningYesNo,
01045                                  i18n("Would you like to accept this "
01046                                       "certificate forever without "
01047                                       "being prompted?"),
01048                                  i18n("Server Authentication"),
01049                                  i18n("&Forever"),
01050                                  i18n("&Current Sessions Only"));
01051              permacache = (result == KMessageBox::Yes);
01052              d->cc->addCertificate(pc, cp, permacache);
01053              d->cc->addHost(pc, ourHost);
01054           } else {
01055              setMetaData("ssl_action", "reject");
01056              rc = -1;
01057              cp = KSSLCertificateCache::Prompt;
01058              d->cc->addCertificate(pc, cp, permacache);
01059           }
01060         }
01061       }
01062     }
01063 
01064 
01065    if (rc == -1) {
01066       return rc;
01067    }
01068 
01069    if (metaData("ssl_activate_warnings") == "TRUE") {
01070    //  - entering SSL
01071    if (!isChild && metaData("ssl_was_in_use") == "FALSE" &&
01072                                         d->kssl->settings()->warnOnEnter()) {
01073      int result;
01074      do {
01075                 result = messageBox(               i18n("You are about to "
01076                                                         "enter secure mode. "
01077                                                         "All transmissions "
01078                                                         "will be encrypted "
01079                                                         "unless otherwise "
01080                                                         "noted.\nThis means "
01081                                                         "that no third party "
01082                                                         "will be able to "
01083                                                         "easily observe your "
01084                                                         "data in transit."),
01085                                                    WarningYesNo,
01086                                                    i18n("Security Information"),
01087                                                    i18n("Display SSL "
01088                                                         "&Information"),
01089                                                    i18n("C&onnect"),
01090                                                    "WarnOnEnterSSLMode" );
01091       // Move this setting into KSSL instead
01092       KConfig *config = new KConfig("kioslaverc");
01093       config->setGroup("Notification Messages");
01094 
01095       if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) {
01096           config->deleteEntry("WarnOnEnterSSLMode");
01097           config->sync();
01098           d->kssl->settings()->setWarnOnEnter(false);
01099           d->kssl->settings()->save();
01100       }
01101       delete config;
01102 
01103       if ( result == KMessageBox::Yes )
01104       {
01105           if (!d->dcc) {
01106              d->dcc = new DCOPClient;
01107              d->dcc->attach();
01108              if (!d->dcc->isApplicationRegistered("kio_uiserver")) {
01109                 KApplication::startServiceByDesktopPath("kio_uiserver.desktop",
01110                 QStringList() );
01111              }
01112           }
01113           QByteArray data, ignore;
01114           QCString ignoretype;
01115           QDataStream arg(data, IO_WriteOnly);
01116           arg << theurl << mOutgoingMetaData;
01117           arg << metaData("window-id").toInt();
01118           d->dcc->call("kio_uiserver", "UIServer",
01119                        "showSSLInfoDialog(QString,KIO::MetaData,int)",
01120                        data, ignoretype, ignore);
01121       }
01122       } while (result != KMessageBox::No);
01123    }
01124 
01125    }   // if ssl_activate_warnings
01126 
01127 
01128    kdDebug(7029) << "SSL connection information follows:" << endl
01129           << "+-----------------------------------------------" << endl
01130           << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
01131           << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
01132           << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
01133           << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
01134           << " of " << d->kssl->connectionInfo().getCipherBits()
01135           << " bits used." << endl
01136           << "| PEER:" << endl
01137           << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
01138           << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
01139           << "| Validation: " << (int)ksv << endl
01140           << "| Certificate matches IP: " << _IPmatchesCN << endl
01141           << "+-----------------------------------------------"
01142           << endl;
01143 
01144    // sendMetaData();  Do not call this function!!
01145    return rc;
01146 }
01147 
01148 
01149 bool TCPSlaveBase::isConnectionValid()
01150 {
01151     if ( m_iSock == -1 )
01152       return false;
01153 
01154     fd_set rdfs;
01155     FD_ZERO(&rdfs);
01156     FD_SET(m_iSock , &rdfs);
01157 
01158     struct timeval tv;
01159     tv.tv_usec = 0;
01160     tv.tv_sec = 0;
01161     int retval;
01162 #ifdef Q_OS_UNIX
01163     do {
01164        retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
01165        if (wasKilled())
01166           return false; // Beam us out of here
01167     } while ((retval == -1) && (errno == EAGAIN));
01168 #else
01169     retval = -1;
01170 #endif
01171     // retval == -1 ==> Error
01172     // retval ==  0 ==> Connection Idle
01173     // retval >=  1 ==> Connection Active
01174     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
01175     //              << retval << endl;
01176 
01177     if (retval == -1)
01178        return false;
01179 
01180     if (retval == 0)
01181        return true;
01182 
01183     // Connection is active, check if it has closed.
01184     char buffer[100];
01185 #ifdef Q_OS_UNIX
01186     do {
01187        retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
01188 
01189     } while ((retval == -1) && (errno == EAGAIN));
01190 #else
01191     retval = -1;
01192 #endif
01193     //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
01194     //                 << retval << endl;
01195     if (retval <= 0)
01196        return false; // Error or connection closed.
01197 
01198     return true; // Connection still valid.
01199 }
01200 
01201 
01202 bool TCPSlaveBase::waitForResponse( int t )
01203 {
01204   fd_set rd;
01205   struct timeval timeout;
01206 
01207   if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
01208     if (d->kssl->pending() > 0)
01209         return true;
01210 
01211   FD_ZERO(&rd);
01212   FD_SET(m_iSock, &rd);
01213 
01214   timeout.tv_usec = 0;
01215   timeout.tv_sec = t;
01216   time_t startTime;
01217 
01218   int rc;
01219   int n = t;
01220 
01221 reSelect:
01222   startTime = time(NULL);
01223 #ifdef Q_OS_UNIX
01224   rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
01225 #else
01226   rc = -1;
01227 #endif
01228   if (wasKilled())
01229     return false; // We're dead.
01230 
01231   if (rc == -1)
01232     return false;
01233 
01234   if (FD_ISSET(m_iSock, &rd))
01235     return true;
01236 
01237   // Well it returned but it wasn't set.  Let's see if it
01238   // returned too early (perhaps from an errant signal) and
01239   // start over with the remaining time
01240   int timeDone = time(NULL) - startTime;
01241   if (timeDone < n)
01242   {
01243     n -= timeDone;
01244     timeout.tv_sec = n;
01245     goto reSelect;
01246   }
01247 
01248   return false; // Timed out!
01249 }
01250 
01251 int TCPSlaveBase::connectResult()
01252 {
01253     return d->status;
01254 }
01255 
01256 void TCPSlaveBase::setBlockConnection( bool b )
01257 {
01258     d->block = b;
01259 }
01260 
01261 void TCPSlaveBase::setConnectTimeout( int t )
01262 {
01263     d->timeout = t;
01264 }
01265 
01266 bool TCPSlaveBase::isSSLTunnelEnabled()
01267 {
01268     return d->useSSLTunneling;
01269 }
01270 
01271 void TCPSlaveBase::setEnableSSLTunnel( bool enable )
01272 {
01273     d->useSSLTunneling = enable;
01274 }
01275 
01276 void TCPSlaveBase::setRealHost( const QString& realHost )
01277 {
01278     d->realHost = realHost;
01279 }
01280 
01281 bool TCPSlaveBase::doSSLHandShake( bool sendError )
01282 {
01283     kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
01284     QString msgHost = d->host;
01285 
01286     d->kssl->reInitialize();
01287 
01288     if (hasMetaData("ssl_session_id")) {
01289         KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
01290         if (s) {
01291             d->kssl->setSession(s);
01292             delete s;
01293     }    
01294     }
01295     certificatePrompt();
01296 
01297     if ( !d->realHost.isEmpty() )
01298     {
01299       msgHost = d->realHost;
01300     }
01301 
01302     kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
01303     d->kssl->setPeerHost(msgHost);
01304 
01305     d->status = d->kssl->connect(m_iSock);
01306     if (d->status < 0)
01307     {
01308         closeDescriptor();
01309         if ( sendError )
01310             error( ERR_COULD_NOT_CONNECT, msgHost);
01311         return false;
01312     }
01313 
01314     setMetaData("ssl_session_id", d->kssl->session()->toString());
01315     setMetaData("ssl_in_use", "TRUE");
01316 
01317     if (!d->kssl->reusingSession()) {
01318         int rc = verifyCertificate();
01319         if ( rc != 1 ) {
01320             d->status = -1;
01321             closeDescriptor();
01322             if ( sendError )
01323                 error( ERR_COULD_NOT_CONNECT, msgHost);
01324             return false;
01325         }
01326     }
01327 
01328     d->needSSLHandShake = false;
01329 
01330     d->savedMetaData = mOutgoingMetaData;
01331     return true;
01332 }
01333 
01334 
01335 bool TCPSlaveBase::userAborted() const
01336 {
01337    return d->userAborted;
01338 }
01339 
01340 void TCPSlaveBase::virtual_hook( int id, void* data )
01341 { SlaveBase::virtual_hook( id, data ); }
01342 
KDE Home | KDE Accessibility Home | Description of Access Keys