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