/* * $Id$ * * Copyright (C) 2000 Alex Zepeda * Copyright (C) 2001 Dawit Alemayehu * * This file is part of the KDE project * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_WS_WIN //temporary #include #endif #include #include #include #include #include #include #include #include "tdeio/tcpslavebase.h" using namespace TDEIO; class TCPSlaveBase::TcpSlaveBasePrivate { public: TcpSlaveBasePrivate() : rblockSz(256), militantSSL(false), userAborted(false) {} ~TcpSlaveBasePrivate() {} KSSL *kssl; bool usingTLS; KSSLCertificateCache *cc; TQString host; TQString realHost; TQString ip; DCOPClient *dcc; KSSLPKCS12 *pkcs; int status; int timeout; int rblockSz; // Size for reading blocks in readLine() bool block; bool useSSLTunneling; bool needSSLHandShake; bool militantSSL; // If true, we just drop a connection silently // if SSL certificate check fails in any way. bool userAborted; MetaData savedMetaData; }; TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort, const TQCString &protocol, const TQCString &poolSocket, const TQCString &appSocket) :SlaveBase (protocol, poolSocket, appSocket), m_iSock(-1), m_iDefaultPort(defaultPort), m_sServiceName(protocol), fp(0) { // We have to have two constructors, so don't add anything // else in here. Put it in doConstructorStuff() instead. doConstructorStuff(); m_bIsSSL = false; } TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort, const TQCString &protocol, const TQCString &poolSocket, const TQCString &appSocket, bool useSSL) :SlaveBase (protocol, poolSocket, appSocket), m_iSock(-1), m_bIsSSL(useSSL), m_iDefaultPort(defaultPort), m_sServiceName(protocol), fp(0) { doConstructorStuff(); if (useSSL) m_bIsSSL = initializeSSL(); } // The constructor procedures go here now. void TCPSlaveBase::doConstructorStuff() { d = new TcpSlaveBasePrivate; d->kssl = 0L; d->ip = ""; d->cc = 0L; d->usingTLS = false; d->dcc = 0L; d->pkcs = 0L; d->status = -1; d->timeout = KProtocolManager::connectTimeout(); d->block = false; d->useSSLTunneling = false; } TCPSlaveBase::~TCPSlaveBase() { cleanSSL(); if (d->usingTLS) delete d->kssl; if (d->dcc) delete d->dcc; if (d->pkcs) delete d->pkcs; delete d; } ssize_t TCPSlaveBase::write(const void *data, ssize_t len) { #ifdef Q_OS_UNIX if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling ) { if ( d->needSSLHandShake ) (void) doSSLHandShake( true ); return d->kssl->write(data, len); } return KSocks::self()->write(m_iSock, data, len); #else return 0; #endif } ssize_t TCPSlaveBase::read(void *data, ssize_t len) { #ifdef Q_OS_UNIX if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling ) { if ( d->needSSLHandShake ) (void) doSSLHandShake( true ); return d->kssl->read(data, len); } return KSocks::self()->read(m_iSock, data, len); #else return 0; #endif } void TCPSlaveBase::setBlockSize(int sz) { if (sz <= 0) sz = 1; d->rblockSz = sz; } ssize_t TCPSlaveBase::readLine(char *data, ssize_t len) { // Optimization: // It's small, but it probably results in a gain on very high // speed connections. I moved 3 if statements out of the while loop // so that the while loop is as small as possible. (GS) // let's not segfault! if (!data) return -1; char tmpbuf[1024]; // 1kb temporary buffer for peeking *data = 0; ssize_t clen = 0; char *buf = data; int rc = 0; if ((m_bIsSSL || d->usingTLS) && !d->useSSLTunneling) { // SSL CASE if ( d->needSSLHandShake ) (void) doSSLHandShake( true ); while (clen < len-1) { rc = d->kssl->pending(); if (rc > 0) { // Read a chunk int bytes = rc; if (bytes > d->rblockSz) bytes = d->rblockSz; rc = d->kssl->peek(tmpbuf, bytes); if (rc <= 0) { // FIXME: this doesn't cover rc == 0 case return -1; } bytes = rc; // in case it contains no \n for (int i = 0; i < rc; i++) { if (tmpbuf[i] == '\n') { bytes = i+1; break; } } if (bytes+clen >= len) // don't read too much! bytes = len - clen - 1; rc = d->kssl->read(buf, bytes); if (rc > 0) { clen += rc; buf += (rc-1); if (*buf++ == '\n') break; } else { // FIXME: different case if rc == 0; return -1; } } else { // Read a byte rc = d->kssl->read(buf, 1); if (rc <= 0) { return -1; // hm rc = 0 then // SSL_read says to call SSL_get_error to see if // this was an error. FIXME } else { clen++; if (*buf++ == '\n') break; } } } } else { // NON SSL CASE while (clen < len-1) { #ifdef Q_OS_UNIX rc = KSocks::self()->read(m_iSock, buf, 1); #else rc = 0; #endif if (rc <= 0) { // FIXME: this doesn't cover rc == 0 case return -1; } else { clen++; if (*buf++ == '\n') break; } } } // Both cases fall through to here *buf = 0; return clen; } unsigned short int TCPSlaveBase::port(unsigned short int _p) { unsigned short int p = _p; if (_p <= 0) { p = m_iDefaultPort; } return p; } // This function is simply a wrapper to establish the connection // to the server. It's a bit more complicated than ::connect // because we first have to check to see if the user specified // a port, and if so use it, otherwise we check to see if there // is a port specified in /etc/services, and if so use that // otherwise as a last resort use the supplied default port. bool TCPSlaveBase::connectToHost( const TQString &host, unsigned int _port, bool sendError ) { #ifdef Q_OS_UNIX unsigned short int p; KExtendedSocket ks; d->userAborted = false; // - leaving SSL - warn before we even connect if (metaData("main_frame_request") == "TRUE" && metaData("ssl_activate_warnings") == "TRUE" && metaData("ssl_was_in_use") == "TRUE" && !m_bIsSSL) { KSSLSettings kss; if (kss.warnOnLeave()) { int result = messageBox( i18n("You are about to leave secure " "mode. Transmissions will no " "longer be encrypted.\nThis " "means that a third party could " "observe your data in transit."), WarningContinueCancel, i18n("Security Information"), i18n("C&ontinue Loading"), TQString::null, "WarnOnLeaveSSLMode" ); // Move this setting into KSSL instead TDEConfig *config = new TDEConfig("tdeioslaverc"); config->setGroup("Notification Messages"); if (!config->readBoolEntry("WarnOnLeaveSSLMode", true)) { config->deleteEntry("WarnOnLeaveSSLMode"); config->sync(); kss.setWarnOnLeave(false); kss.save(); } delete config; if ( result == KMessageBox::Cancel ) { d->userAborted = true; return false; } } } d->status = -1; d->host = host; d->needSSLHandShake = m_bIsSSL; p = port(_port); ks.setAddress(host, p); if ( d->timeout > -1 ) ks.setTimeout( d->timeout ); if (ks.connect() < 0) { d->status = ks.status(); if ( sendError ) { if (d->status == IO_LookupError) error( ERR_UNKNOWN_HOST, host); else if ( d->status != -1 ) error( ERR_COULD_NOT_CONNECT, host); } return false; } m_iSock = ks.fd(); // store the IP for later const TDESocketAddress *sa = ks.peerAddress(); if (sa) d->ip = sa->nodeName(); else d->ip = ""; ks.release(); // KExtendedSocket no longer applicable if ( d->block != ks.blockingMode() ) ks.setBlockingMode( d->block ); m_iPort=p; if (m_bIsSSL && !d->useSSLTunneling) { if ( !doSSLHandShake( sendError ) ) return false; } else setMetaData("ssl_in_use", "FALSE"); // Since we want to use stdio on the socket, // we must fdopen it to get a file pointer, // if it fails, close everything up if ((fp = KDE_fdopen(m_iSock, "w+")) == 0) { closeDescriptor(); return false; } return true; #else //!Q_OS_UNIX return false; #endif //Q_OS_UNIX } void TCPSlaveBase::closeDescriptor() { stopTLS(); if (fp) { fclose(fp); fp=0; m_iSock=-1; if (m_bIsSSL) d->kssl->close(); } if (m_iSock != -1) { close(m_iSock); m_iSock=-1; } d->ip = ""; d->host = ""; } bool TCPSlaveBase::initializeSSL() { if (m_bIsSSL) { if (KSSL::doesSSLWork()) { d->kssl = new KSSL; return true; } } return false; } void TCPSlaveBase::cleanSSL() { delete d->cc; if (m_bIsSSL) { delete d->kssl; d->kssl = 0; } d->militantSSL = false; } bool TCPSlaveBase::atEnd() { return feof(fp); } int TCPSlaveBase::startTLS() { if (d->usingTLS || d->useSSLTunneling || m_bIsSSL || !KSSL::doesSSLWork()) return false; d->kssl = new KSSL(false); if (!d->kssl->TLSInit()) { delete d->kssl; return -1; } if ( !d->realHost.isEmpty() ) { kdDebug(7029) << "Setting real hostname: " << d->realHost << endl; d->kssl->setPeerHost(d->realHost); } else { kdDebug(7029) << "Setting real hostname: " << d->host << endl; d->kssl->setPeerHost(d->host); } if (hasMetaData("ssl_session_id")) { KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id")); if (s) { d->kssl->setSession(s); delete s; } } certificatePrompt(); int rc = d->kssl->connect(m_iSock); if (rc < 0) { delete d->kssl; return -2; } setMetaData("ssl_session_id", d->kssl->session()->toString()); d->usingTLS = true; setMetaData("ssl_in_use", "TRUE"); if (!d->kssl->reusingSession()) { rc = verifyCertificate(); if (rc != 1) { setMetaData("ssl_in_use", "FALSE"); d->usingTLS = false; delete d->kssl; return -3; } } d->savedMetaData = mOutgoingMetaData; return (d->usingTLS ? 1 : 0); } void TCPSlaveBase::stopTLS() { if (d->usingTLS) { delete d->kssl; d->usingTLS = false; setMetaData("ssl_in_use", "FALSE"); } } void TCPSlaveBase::setSSLMetaData() { if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL)) return; mOutgoingMetaData = d->savedMetaData; } bool TCPSlaveBase::canUseTLS() { if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork()) return false; KSSLSettings kss; return kss.tlsv1(); } void TCPSlaveBase::certificatePrompt() { TQString certname; // the cert to use this session bool send = false, prompt = false, save = false, forcePrompt = false; KSSLCertificateHome::KSSLAuthAction aa; setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed if (metaData("ssl_no_client_cert") == "TRUE") return; forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE"); // Delete the old cert since we're certainly done with it now if (d->pkcs) { delete d->pkcs; d->pkcs = NULL; } if (!d->kssl) return; // Look for a general certificate if (!forcePrompt) { certname = KSSLCertificateHome::getDefaultCertificateName(&aa); switch(aa) { case KSSLCertificateHome::AuthSend: send = true; prompt = false; break; case KSSLCertificateHome::AuthDont: send = false; prompt = false; certname = TQString::null; break; case KSSLCertificateHome::AuthPrompt: send = false; prompt = true; break; default: break; } } TQString ourHost; if (!d->realHost.isEmpty()) { ourHost = d->realHost; } else { ourHost = d->host; } // Look for a certificate on a per-host basis as an override TQString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa); if (aa != KSSLCertificateHome::AuthNone) { // we must override switch (aa) { case KSSLCertificateHome::AuthSend: send = true; prompt = false; certname = tmpcn; break; case KSSLCertificateHome::AuthDont: send = false; prompt = false; certname = TQString::null; break; case KSSLCertificateHome::AuthPrompt: send = false; prompt = true; certname = tmpcn; break; default: break; } } // Finally, we allow the application to override anything. if (hasMetaData("ssl_demand_certificate")) { certname = metaData("ssl_demand_certificate"); if (!certname.isEmpty()) { forcePrompt = false; prompt = false; send = true; } } if (certname.isEmpty() && !prompt && !forcePrompt) return; // Ok, we're supposed to prompt the user.... if (prompt || forcePrompt) { TQStringList certs = KSSLCertificateHome::getCertificateList(); for (TQStringList::Iterator it = certs.begin(); it != certs.end(); ++it) { KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it); if (pkcs && (!pkcs->getCertificate() || !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) { certs.remove(*it); } delete pkcs; } if (certs.isEmpty()) return; // we had nothing else, and prompt failed if (!d->dcc) { d->dcc = new DCOPClient; d->dcc->attach(); if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) { TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop", TQStringList() ); } } TQByteArray data, retval; TQCString rettype; TQDataStream arg(data, IO_WriteOnly); arg << ourHost; arg << certs; arg << metaData("window-id").toInt(); bool rc = d->dcc->call("tdeio_uiserver", "UIServer", "showSSLCertDialog(TQString, TQStringList,int)", data, rettype, retval); if (rc && rettype == "KSSLCertDlgRet") { TQDataStream retStream(retval, IO_ReadOnly); KSSLCertDlgRet drc; retStream >> drc; if (drc.ok) { send = drc.send; save = drc.save; certname = drc.choice; } } } // The user may have said to not send the certificate, // but to save the choice if (!send) { if (save) { KSSLCertificateHome::setDefaultCertificate(certname, ourHost, false, false); } return; } // We're almost committed. If we can read the cert, we'll send it now. KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname); if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) { // We need the password TDEIO::AuthInfo ai; bool first = true; do { ai.prompt = i18n("Enter the certificate password:"); ai.caption = i18n("SSL Certificate Password"); ai.url.setProtocol("kssl"); ai.url.setHost(certname); ai.username = certname; ai.keepPassword = true; bool showprompt; if (first) showprompt = !checkCachedAuthentication(ai); else showprompt = true; if (showprompt) { if (!openPassDlg(ai, first ? TQString::null : i18n("Unable to open the certificate. Try a new password?"))) break; } first = false; pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password); } while (!pkcs); } // If we could open the certificate, let's send it if (pkcs) { if (!d->kssl->setClientCertificate(pkcs)) { messageBox(Information, i18n("The procedure to set the " "client certificate for the session " "failed."), i18n("SSL")); delete pkcs; // we don't need this anymore pkcs = 0L; } else { kdDebug(7029) << "Client SSL certificate is being used." << endl; setMetaData("ssl_using_client_cert", "TRUE"); if (save) { KSSLCertificateHome::setDefaultCertificate(certname, ourHost, true, false); } } d->pkcs = pkcs; } } bool TCPSlaveBase::usingTLS() const { return d->usingTLS; } // ### remove this for KDE4 (misses const): bool TCPSlaveBase::usingTLS() { return d->usingTLS; } // Returns 0 for failed verification, -1 for rejected cert and 1 for ok int TCPSlaveBase::verifyCertificate() { int rc = 0; bool permacache = false; bool isChild = false; bool _IPmatchesCN = false; int result; bool doAddHost = false; TQString ourHost; if (!d->realHost.isEmpty()) ourHost = d->realHost; else ourHost = d->host; TQString theurl = TQString(m_sServiceName)+"://"+ourHost+":"+TQString::number(m_iPort); if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE") d->militantSSL = false; else if (metaData("ssl_militant") == "TRUE") d->militantSSL = true; if (!d->cc) d->cc = new KSSLCertificateCache; KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate(); KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer); _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress(); if (!_IPmatchesCN) { #ifndef Q_WS_WIN //temporary KNetwork::KResolverResults res = KNetwork::KResolver::resolve(d->kssl->peerInfo().peerHost(), "80", KNetwork::KResolver::CanonName); if (!res.isEmpty()) { TQString old = d->kssl->peerInfo().peerHost(); d->kssl->peerInfo().setPeerHost(res[0].canonicalName()); _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress(); if (!_IPmatchesCN) { d->kssl->peerInfo().setPeerHost(old); } } #endif if (!_IPmatchesCN && !d->militantSSL) { // force this if the user wants it if (d->cc->getHostList(pc).contains(ourHost)) { _IPmatchesCN = true; } } } if (!_IPmatchesCN) { ksvl << KSSLCertificate::InvalidHost; } KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok; if (!ksvl.isEmpty()) ksv = ksvl.first(); /* Setting the various bits of meta-info that will be needed. */ setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher()); setMetaData("ssl_cipher_desc", d->kssl->connectionInfo().getCipherDescription()); setMetaData("ssl_cipher_version", d->kssl->connectionInfo().getCipherVersion()); setMetaData("ssl_cipher_used_bits", TQString::number(d->kssl->connectionInfo().getCipherUsedBits())); setMetaData("ssl_cipher_bits", TQString::number(d->kssl->connectionInfo().getCipherBits())); setMetaData("ssl_peer_ip", d->ip); if (!d->realHost.isEmpty()) { setMetaData("ssl_proxied", "true"); } TQString errorStr; for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin(); it != ksvl.end(); ++it) { errorStr += TQString::number(*it)+":"; } setMetaData("ssl_cert_errors", errorStr); setMetaData("ssl_peer_certificate", pc.toString()); if (pc.chain().isValid() && pc.chain().depth() > 1) { TQString theChain; TQPtrList chain = pc.chain().getChain(); chain.setAutoDelete(true); for (KSSLCertificate *c = chain.first(); c; c = chain.next()) { theChain += c->toString(); theChain += "\n"; } setMetaData("ssl_peer_chain", theChain); } else setMetaData("ssl_peer_chain", ""); setMetaData("ssl_cert_state", TQString::number(ksv)); if (ksv == KSSLCertificate::Ok) { rc = 1; setMetaData("ssl_action", "accept"); } kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl; if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") { // Since we're the parent, we need to teach the child. setMetaData("ssl_parent_ip", d->ip); setMetaData("ssl_parent_cert", pc.toString()); // - Read from cache and see if there is a policy for this KSSLCertificateCache::KSSLCertificatePolicy cp = d->cc->getPolicyByCertificate(pc); // - validation code if (ksv != KSSLCertificate::Ok) { if (d->militantSSL) { return -1; } if (cp == KSSLCertificateCache::Unknown || cp == KSSLCertificateCache::Ambiguous) { cp = KSSLCertificateCache::Prompt; } else { // A policy was already set so let's honor that. permacache = d->cc->isPermanent(pc); } /* if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) { cp = KSSLCertificateCache::Prompt; // ksv = KSSLCertificate::Ok; } */ // Precondition: cp is one of Reject, Accept or Prompt switch (cp) { case KSSLCertificateCache::Accept: rc = 1; setMetaData("ssl_action", "accept"); break; case KSSLCertificateCache::Reject: rc = -1; setMetaData("ssl_action", "reject"); break; case KSSLCertificateCache::Prompt: { do { if (ksv == KSSLCertificate::InvalidHost) { TQString msg = i18n("The IP address of the host %1 " "does not match the one the " "certificate was issued to."); result = messageBox( WarningYesNoCancel, msg.arg(ourHost), i18n("Server Authentication"), i18n("&Details"), i18n("Co&ntinue") ); } else { TQString msg = i18n("The server certificate failed the " "authenticity test (%1)."); result = messageBox( WarningYesNoCancel, msg.arg(ourHost), i18n("Server Authentication"), i18n("&Details"), i18n("Co&ntinue") ); } if (result == KMessageBox::Yes) { if (!d->dcc) { d->dcc = new DCOPClient; d->dcc->attach(); if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) { TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop", TQStringList() ); } } TQByteArray data, ignore; TQCString ignoretype; TQDataStream arg(data, IO_WriteOnly); arg << theurl << mOutgoingMetaData; arg << metaData("window-id").toInt(); d->dcc->call("tdeio_uiserver", "UIServer", "showSSLInfoDialog(TQString,TDEIO::MetaData,int)", data, ignoretype, ignore); } } while (result == KMessageBox::Yes); if (result == KMessageBox::No) { setMetaData("ssl_action", "accept"); rc = 1; cp = KSSLCertificateCache::Accept; doAddHost = true; result = messageBox( WarningYesNo, i18n("Would you like to accept this " "certificate forever without " "being prompted?"), i18n("Server Authentication"), i18n("&Forever"), i18n("&Current Sessions Only")); if (result == KMessageBox::Yes) permacache = true; else permacache = false; } else { setMetaData("ssl_action", "reject"); rc = -1; cp = KSSLCertificateCache::Prompt; } break; } default: kdDebug(7029) << "TCPSlaveBase/SSL error in cert code." << "Please report this to kfm-devel@kde.org." << endl; break; } } // - cache the results d->cc->addCertificate(pc, cp, permacache); if (doAddHost) d->cc->addHost(pc, ourHost); } else { // Child frame // - Read from cache and see if there is a policy for this KSSLCertificateCache::KSSLCertificatePolicy cp = d->cc->getPolicyByCertificate(pc); isChild = true; // Check the cert and IP to make sure they're the same // as the parent frame bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") && pc.toString() == metaData("ssl_parent_cert")); if (ksv == KSSLCertificate::Ok) { if (certAndIPTheSame) { // success rc = 1; setMetaData("ssl_action", "accept"); } else { /* if (d->militantSSL) { return -1; } result = messageBox(WarningYesNo, i18n("The certificate is valid but does not appear to have been assigned to this server. Do you wish to continue loading?"), i18n("Server Authentication")); if (result == KMessageBox::Yes) { // success rc = 1; setMetaData("ssl_action", "accept"); } else { // fail rc = -1; setMetaData("ssl_action", "reject"); } */ setMetaData("ssl_action", "accept"); rc = 1; // Let's accept this now. It's bad, but at least the user // will see potential attacks in KDE3 with the pseudo-lock // icon on the toolbar, and can investigate with the RMB } } else { if (d->militantSSL) { return -1; } if (cp == KSSLCertificateCache::Accept) { if (certAndIPTheSame) { // success rc = 1; setMetaData("ssl_action", "accept"); } else { // fail result = messageBox(WarningYesNo, 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?"), i18n("Server Authentication")); if (result == KMessageBox::Yes) { rc = 1; setMetaData("ssl_action", "accept"); d->cc->addHost(pc, ourHost); } else { rc = -1; setMetaData("ssl_action", "reject"); } } } else if (cp == KSSLCertificateCache::Reject) { // fail messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the Trinity Control Center."), i18n("Server Authentication")); rc = -1; setMetaData("ssl_action", "reject"); } else { do { TQString msg = i18n("The server certificate failed the " "authenticity test (%1)."); result = messageBox(WarningYesNoCancel, msg.arg(ourHost), i18n("Server Authentication"), i18n("&Details"), i18n("Co&nnect")); if (result == KMessageBox::Yes) { if (!d->dcc) { d->dcc = new DCOPClient; d->dcc->attach(); if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) { TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop", TQStringList() ); } } TQByteArray data, ignore; TQCString ignoretype; TQDataStream arg(data, IO_WriteOnly); arg << theurl << mOutgoingMetaData; arg << metaData("window-id").toInt(); d->dcc->call("tdeio_uiserver", "UIServer", "showSSLInfoDialog(TQString,TDEIO::MetaData,int)", data, ignoretype, ignore); } } while (result == KMessageBox::Yes); if (result == KMessageBox::No) { setMetaData("ssl_action", "accept"); rc = 1; cp = KSSLCertificateCache::Accept; result = messageBox(WarningYesNo, i18n("Would you like to accept this " "certificate forever without " "being prompted?"), i18n("Server Authentication"), i18n("&Forever"), i18n("&Current Sessions Only")); permacache = (result == KMessageBox::Yes); d->cc->addCertificate(pc, cp, permacache); d->cc->addHost(pc, ourHost); } else { setMetaData("ssl_action", "reject"); rc = -1; cp = KSSLCertificateCache::Prompt; d->cc->addCertificate(pc, cp, permacache); } } } } if (rc == -1) { return rc; } if (metaData("ssl_activate_warnings") == "TRUE") { // - entering SSL if (!isChild && metaData("ssl_was_in_use") == "FALSE" && d->kssl->settings()->warnOnEnter()) { int result; do { result = messageBox( i18n("You are about to " "enter secure mode. " "All transmissions " "will be encrypted " "unless otherwise " "noted.\nThis means " "that no third party " "will be able to " "easily observe your " "data in transit."), WarningYesNo, i18n("Security Information"), i18n("Display SSL " "&Information"), i18n("C&onnect"), "WarnOnEnterSSLMode" ); // Move this setting into KSSL instead TDEConfig *config = new TDEConfig("tdeioslaverc"); config->setGroup("Notification Messages"); if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) { config->deleteEntry("WarnOnEnterSSLMode"); config->sync(); d->kssl->settings()->setWarnOnEnter(false); d->kssl->settings()->save(); } delete config; if ( result == KMessageBox::Yes ) { if (!d->dcc) { d->dcc = new DCOPClient; d->dcc->attach(); if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) { TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop", TQStringList() ); } } TQByteArray data, ignore; TQCString ignoretype; TQDataStream arg(data, IO_WriteOnly); arg << theurl << mOutgoingMetaData; arg << metaData("window-id").toInt(); d->dcc->call("tdeio_uiserver", "UIServer", "showSSLInfoDialog(TQString,TDEIO::MetaData,int)", data, ignoretype, ignore); } } while (result != KMessageBox::No); } } // if ssl_activate_warnings kdDebug(7029) << "SSL connection information follows:" << endl << "+-----------------------------------------------" << endl << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits() << " of " << d->kssl->connectionInfo().getCipherBits() << " bits used." << endl << "| PEER:" << endl << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl << "| Validation: " << (int)ksv << endl << "| Certificate matches IP: " << _IPmatchesCN << endl << "+-----------------------------------------------" << endl; // sendMetaData(); Do not call this function!! return rc; } bool TCPSlaveBase::isConnectionValid() { if ( m_iSock == -1 ) return false; fd_set rdfs; FD_ZERO(&rdfs); FD_SET(m_iSock , &rdfs); struct timeval tv; tv.tv_usec = 0; tv.tv_sec = 0; int retval; #ifdef Q_OS_UNIX do { retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv); if (wasKilled()) return false; // Beam us out of here } while ((retval == -1) && (errno == EAGAIN)); #else retval = -1; #endif // retval == -1 ==> Error // retval == 0 ==> Connection Idle // retval >= 1 ==> Connection Active //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: " // << retval << endl; if (retval == -1) return false; if (retval == 0) return true; // Connection is active, check if it has closed. char buffer[100]; #ifdef Q_OS_UNIX do { retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK); } while ((retval == -1) && (errno == EAGAIN)); #else retval = -1; #endif //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: " // << retval << endl; if (retval <= 0) return false; // Error or connection closed. return true; // Connection still valid. } bool TCPSlaveBase::waitForResponse( int t ) { fd_set rd; struct timeval timeout; if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl ) if (d->kssl->pending() > 0) return true; FD_ZERO(&rd); FD_SET(m_iSock, &rd); timeout.tv_usec = 0; timeout.tv_sec = t; time_t startTime; int rc; int n = t; reSelect: startTime = time(NULL); #ifdef Q_OS_UNIX rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout); #else rc = -1; #endif if (wasKilled()) return false; // We're dead. if (rc == -1) return false; if (FD_ISSET(m_iSock, &rd)) return true; // Well it returned but it wasn't set. Let's see if it // returned too early (perhaps from an errant signal) and // start over with the remaining time int timeDone = time(NULL) - startTime; if (timeDone < n) { n -= timeDone; timeout.tv_sec = n; goto reSelect; } return false; // Timed out! } int TCPSlaveBase::connectResult() { return d->status; } void TCPSlaveBase::setBlockConnection( bool b ) { d->block = b; } void TCPSlaveBase::setConnectTimeout( int t ) { d->timeout = t; } bool TCPSlaveBase::isSSLTunnelEnabled() { return d->useSSLTunneling; } void TCPSlaveBase::setEnableSSLTunnel( bool enable ) { d->useSSLTunneling = enable; } void TCPSlaveBase::setRealHost( const TQString& realHost ) { d->realHost = realHost; } bool TCPSlaveBase::doSSLHandShake( bool sendError ) { kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl; TQString msgHost = d->host; d->kssl->reInitialize(); if (hasMetaData("ssl_session_id")) { KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id")); if (s) { d->kssl->setSession(s); delete s; } } certificatePrompt(); if ( !d->realHost.isEmpty() ) { msgHost = d->realHost; } kdDebug(7029) << "Setting real hostname: " << msgHost << endl; d->kssl->setPeerHost(msgHost); d->status = d->kssl->connect(m_iSock); if (d->status < 0) { closeDescriptor(); if ( sendError ) error( ERR_COULD_NOT_CONNECT, msgHost); return false; } setMetaData("ssl_session_id", d->kssl->session()->toString()); setMetaData("ssl_in_use", "TRUE"); if (!d->kssl->reusingSession()) { int rc = verifyCertificate(); if ( rc != 1 ) { d->status = -1; closeDescriptor(); if ( sendError ) error( ERR_COULD_NOT_CONNECT, msgHost); return false; } } d->needSSLHandShake = false; d->savedMetaData = mOutgoingMetaData; return true; } bool TCPSlaveBase::userAborted() const { return d->userAborted; } void TCPSlaveBase::virtual_hook( int id, void* data ) { SlaveBase::virtual_hook( id, data ); }