• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdeio/tdeio
 

tdeio/tdeio

tcpslavebase.cpp

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

tdeio/tdeio

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

tdeio/tdeio

Skip menu "tdeio/tdeio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdeio/tdeio by doxygen 1.6.3
This website is maintained by Timothy Pearson.