tcpslavebase.cpp

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