tcpslavebase.cpp
00001 /* 00002 * $Id: tcpslavebase.cpp 662342 2007-05-07 22:26:49Z dfaure $ 00003 * 00004 * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net 00005 * Copyright (C) 2001-2003 George Staikos <staikos@kde.org> 00006 * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org> 00007 * 00008 * This file is part of the KDE project 00009 * 00010 * This library is free software; you can redistribute it and/or 00011 * modify it under the terms of the GNU Library General Public 00012 * License as published by the Free Software Foundation; either 00013 * version 2 of the License, or (at your option) any later version. 00014 * 00015 * This library is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 * Library General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU Library General Public License 00021 * along with this library; see the file COPYING.LIB. If not, write to 00022 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00023 * Boston, MA 02110-1301, USA. 00024 */ 00025 00026 #ifdef HAVE_CONFIG_H 00027 #include <config.h> 00028 #endif 00029 00030 #include <sys/types.h> 00031 #include <sys/uio.h> 00032 #include <sys/time.h> 00033 #include <sys/socket.h> 00034 00035 #include <netinet/in.h> 00036 00037 #include <time.h> 00038 #include <netdb.h> 00039 #include <unistd.h> 00040 #include <errno.h> 00041 00042 #include <ksocks.h> 00043 #include <kdebug.h> 00044 #include <ksslall.h> 00045 #include <ksslcertdlg.h> 00046 #include <kmessagebox.h> 00047 #ifndef Q_WS_WIN //temporary 00048 #include <kresolver.h> 00049 #endif 00050 00051 #include <klocale.h> 00052 #include <dcopclient.h> 00053 #include <qcstring.h> 00054 #include <qdatastream.h> 00055 00056 #include <kapplication.h> 00057 00058 #include <kprotocolmanager.h> 00059 #include <kde_file.h> 00060 00061 #include "kio/tcpslavebase.h" 00062 00063 using namespace KIO; 00064 00065 class TCPSlaveBase::TcpSlaveBasePrivate 00066 { 00067 public: 00068 00069 TcpSlaveBasePrivate() : rblockSz(256), militantSSL(false), userAborted(false) {} 00070 ~TcpSlaveBasePrivate() {} 00071 00072 KSSL *kssl; 00073 bool usingTLS; 00074 KSSLCertificateCache *cc; 00075 QString host; 00076 QString realHost; 00077 QString ip; 00078 DCOPClient *dcc; 00079 KSSLPKCS12 *pkcs; 00080 00081 int status; 00082 int timeout; 00083 int rblockSz; // Size for reading blocks in readLine() 00084 bool block; 00085 bool useSSLTunneling; 00086 bool needSSLHandShake; 00087 bool militantSSL; // If true, we just drop a connection silently 00088 // if SSL certificate check fails in any way. 00089 bool userAborted; 00090 MetaData savedMetaData; 00091 }; 00092 00093 00094 TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort, 00095 const QCString &protocol, 00096 const QCString &poolSocket, 00097 const QCString &appSocket) 00098 :SlaveBase (protocol, poolSocket, appSocket), 00099 m_iSock(-1), 00100 m_iDefaultPort(defaultPort), 00101 m_sServiceName(protocol), 00102 fp(0) 00103 { 00104 // We have to have two constructors, so don't add anything 00105 // else in here. Put it in doConstructorStuff() instead. 00106 doConstructorStuff(); 00107 m_bIsSSL = false; 00108 } 00109 00110 TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort, 00111 const QCString &protocol, 00112 const QCString &poolSocket, 00113 const QCString &appSocket, 00114 bool useSSL) 00115 :SlaveBase (protocol, poolSocket, appSocket), 00116 m_iSock(-1), 00117 m_bIsSSL(useSSL), 00118 m_iDefaultPort(defaultPort), 00119 m_sServiceName(protocol), 00120 fp(0) 00121 { 00122 doConstructorStuff(); 00123 if (useSSL) 00124 m_bIsSSL = initializeSSL(); 00125 } 00126 00127 // The constructor procedures go here now. 00128 void TCPSlaveBase::doConstructorStuff() 00129 { 00130 d = new TcpSlaveBasePrivate; 00131 d->kssl = 0L; 00132 d->ip = ""; 00133 d->cc = 0L; 00134 d->usingTLS = false; 00135 d->dcc = 0L; 00136 d->pkcs = 0L; 00137 d->status = -1; 00138 d->timeout = KProtocolManager::connectTimeout(); 00139 d->block = false; 00140 d->useSSLTunneling = false; 00141 } 00142 00143 TCPSlaveBase::~TCPSlaveBase() 00144 { 00145 cleanSSL(); 00146 if (d->usingTLS) delete d->kssl; 00147 if (d->dcc) delete d->dcc; 00148 if (d->pkcs) delete d->pkcs; 00149 delete d; 00150 } 00151 00152 ssize_t TCPSlaveBase::write(const void *data, ssize_t len) 00153 { 00154 #ifdef Q_OS_UNIX 00155 if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling ) 00156 { 00157 if ( d->needSSLHandShake ) 00158 (void) doSSLHandShake( true ); 00159 return d->kssl->write(data, len); 00160 } 00161 return KSocks::self()->write(m_iSock, data, len); 00162 #else 00163 return 0; 00164 #endif 00165 } 00166 00167 ssize_t TCPSlaveBase::read(void *data, ssize_t len) 00168 { 00169 #ifdef Q_OS_UNIX 00170 if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling ) 00171 { 00172 if ( d->needSSLHandShake ) 00173 (void) doSSLHandShake( true ); 00174 return d->kssl->read(data, len); 00175 } 00176 return KSocks::self()->read(m_iSock, data, len); 00177 #else 00178 return 0; 00179 #endif 00180 } 00181 00182 00183 void TCPSlaveBase::setBlockSize(int sz) 00184 { 00185 if (sz <= 0) 00186 sz = 1; 00187 00188 d->rblockSz = sz; 00189 } 00190 00191 00192 ssize_t TCPSlaveBase::readLine(char *data, ssize_t len) 00193 { 00194 // Optimization: 00195 // It's small, but it probably results in a gain on very high 00196 // speed connections. I moved 3 if statements out of the while loop 00197 // so that the while loop is as small as possible. (GS) 00198 00199 // let's not segfault! 00200 if (!data) 00201 return -1; 00202 00203 char tmpbuf[1024]; // 1kb temporary buffer for peeking 00204 *data = 0; 00205 ssize_t clen = 0; 00206 char *buf = data; 00207 int rc = 0; 00208 00209 if ((m_bIsSSL || d->usingTLS) && !d->useSSLTunneling) { // SSL CASE 00210 if ( d->needSSLHandShake ) 00211 (void) doSSLHandShake( true ); 00212 00213 while (clen < len-1) { 00214 rc = d->kssl->pending(); 00215 if (rc > 0) { // Read a chunk 00216 int bytes = rc; 00217 if (bytes > d->rblockSz) 00218 bytes = d->rblockSz; 00219 00220 rc = d->kssl->peek(tmpbuf, bytes); 00221 if (rc <= 0) { 00222 // FIXME: this doesn't cover rc == 0 case 00223 return -1; 00224 } 00225 00226 bytes = rc; // in case it contains no \n 00227 for (int i = 0; i < rc; i++) { 00228 if (tmpbuf[i] == '\n') { 00229 bytes = i+1; 00230 break; 00231 } 00232 } 00233 00234 if (bytes+clen >= len) // don't read too much! 00235 bytes = len - clen - 1; 00236 00237 rc = d->kssl->read(buf, bytes); 00238 if (rc > 0) { 00239 clen += rc; 00240 buf += (rc-1); 00241 if (*buf++ == '\n') 00242 break; 00243 } else { 00244 // FIXME: different case if rc == 0; 00245 return -1; 00246 } 00247 } else { // Read a byte 00248 rc = d->kssl->read(buf, 1); 00249 if (rc <= 0) { 00250 return -1; 00251 // hm rc = 0 then 00252 // SSL_read says to call SSL_get_error to see if 00253 // this was an error. FIXME 00254 } else { 00255 clen++; 00256 if (*buf++ == '\n') 00257 break; 00258 } 00259 } 00260 } 00261 } else { // NON SSL CASE 00262 while (clen < len-1) { 00263 #ifdef Q_OS_UNIX 00264 rc = KSocks::self()->read(m_iSock, buf, 1); 00265 #else 00266 rc = 0; 00267 #endif 00268 if (rc <= 0) { 00269 // FIXME: this doesn't cover rc == 0 case 00270 return -1; 00271 } else { 00272 clen++; 00273 if (*buf++ == '\n') 00274 break; 00275 } 00276 } 00277 } 00278 00279 // Both cases fall through to here 00280 *buf = 0; 00281 return clen; 00282 } 00283 00284 unsigned short int TCPSlaveBase::port(unsigned short int _p) 00285 { 00286 unsigned short int p = _p; 00287 00288 if (_p <= 0) 00289 { 00290 p = m_iDefaultPort; 00291 } 00292 00293 return p; 00294 } 00295 00296 // This function is simply a wrapper to establish the connection 00297 // to the server. It's a bit more complicated than ::connect 00298 // because we first have to check to see if the user specified 00299 // a port, and if so use it, otherwise we check to see if there 00300 // is a port specified in /etc/services, and if so use that 00301 // otherwise as a last resort use the supplied default port. 00302 bool TCPSlaveBase::connectToHost( const QString &host, 00303 unsigned int _port, 00304 bool sendError ) 00305 { 00306 #ifdef Q_OS_UNIX 00307 unsigned short int p; 00308 KExtendedSocket ks; 00309 00310 d->userAborted = false; 00311 00312 // - leaving SSL - warn before we even connect 00313 if (metaData("main_frame_request") == "TRUE" && 00314 metaData("ssl_activate_warnings") == "TRUE" && 00315 metaData("ssl_was_in_use") == "TRUE" && 00316 !m_bIsSSL) { 00317 KSSLSettings kss; 00318 if (kss.warnOnLeave()) { 00319 int result = messageBox( i18n("You are about to leave secure " 00320 "mode. Transmissions will no " 00321 "longer be encrypted.\nThis " 00322 "means that a third party could " 00323 "observe your data in transit."), 00324 WarningContinueCancel, 00325 i18n("Security Information"), 00326 i18n("C&ontinue Loading"), QString::null, 00327 "WarnOnLeaveSSLMode" ); 00328 00329 // Move this setting into KSSL instead 00330 KConfig *config = new KConfig("kioslaverc"); 00331 config->setGroup("Notification Messages"); 00332 00333 if (!config->readBoolEntry("WarnOnLeaveSSLMode", true)) { 00334 config->deleteEntry("WarnOnLeaveSSLMode"); 00335 config->sync(); 00336 kss.setWarnOnLeave(false); 00337 kss.save(); 00338 } 00339 delete config; 00340 00341 if ( result == KMessageBox::Cancel ) { 00342 d->userAborted = true; 00343 return false; 00344 } 00345 } 00346 } 00347 00348 d->status = -1; 00349 d->host = host; 00350 d->needSSLHandShake = m_bIsSSL; 00351 p = port(_port); 00352 ks.setAddress(host, p); 00353 if ( d->timeout > -1 ) 00354 ks.setTimeout( d->timeout ); 00355 00356 if (ks.connect() < 0) 00357 { 00358 d->status = ks.status(); 00359 if ( sendError ) 00360 { 00361 if (d->status == IO_LookupError) 00362 error( ERR_UNKNOWN_HOST, host); 00363 else if ( d->status != -1 ) 00364 error( ERR_COULD_NOT_CONNECT, host); 00365 } 00366 return false; 00367 } 00368 00369 m_iSock = ks.fd(); 00370 00371 // store the IP for later 00372 const KSocketAddress *sa = ks.peerAddress(); 00373 if (sa) 00374 d->ip = sa->nodeName(); 00375 else 00376 d->ip = ""; 00377 00378 ks.release(); // KExtendedSocket no longer applicable 00379 00380 if ( d->block != ks.blockingMode() ) 00381 ks.setBlockingMode( d->block ); 00382 00383 m_iPort=p; 00384 00385 if (m_bIsSSL && !d->useSSLTunneling) { 00386 if ( !doSSLHandShake( sendError ) ) 00387 return false; 00388 } 00389 else 00390 setMetaData("ssl_in_use", "FALSE"); 00391 00392 // Since we want to use stdio on the socket, 00393 // we must fdopen it to get a file pointer, 00394 // if it fails, close everything up 00395 if ((fp = KDE_fdopen(m_iSock, "w+")) == 0) { 00396 closeDescriptor(); 00397 return false; 00398 } 00399 00400 return true; 00401 #else //!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->setSession(s); 00475 delete s; 00476 } 00477 } 00478 certificatePrompt(); 00479 00480 int rc = d->kssl->connect(m_iSock); 00481 if (rc < 0) { 00482 delete d->kssl; 00483 return -2; 00484 } 00485 00486 setMetaData("ssl_session_id", d->kssl->session()->toString()); 00487 00488 d->usingTLS = true; 00489 setMetaData("ssl_in_use", "TRUE"); 00490 00491 if (!d->kssl->reusingSession()) { 00492 rc = verifyCertificate(); 00493 if (rc != 1) { 00494 setMetaData("ssl_in_use", "FALSE"); 00495 d->usingTLS = false; 00496 delete d->kssl; 00497 return -3; 00498 } 00499 } 00500 00501 d->savedMetaData = mOutgoingMetaData; 00502 return (d->usingTLS ? 1 : 0); 00503 } 00504 00505 00506 void TCPSlaveBase::stopTLS() 00507 { 00508 if (d->usingTLS) { 00509 delete d->kssl; 00510 d->usingTLS = false; 00511 setMetaData("ssl_in_use", "FALSE"); 00512 } 00513 } 00514 00515 00516 void TCPSlaveBase::setSSLMetaData() { 00517 if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL)) 00518 return; 00519 00520 mOutgoingMetaData = d->savedMetaData; 00521 } 00522 00523 00524 bool TCPSlaveBase::canUseTLS() 00525 { 00526 if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork()) 00527 return false; 00528 00529 KSSLSettings kss; 00530 return kss.tlsv1(); 00531 } 00532 00533 00534 void TCPSlaveBase::certificatePrompt() 00535 { 00536 QString certname; // the cert to use this session 00537 bool send = false, prompt = false, save = false, forcePrompt = false; 00538 KSSLCertificateHome::KSSLAuthAction aa; 00539 00540 setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed 00541 00542 if (metaData("ssl_no_client_cert") == "TRUE") return; 00543 forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE"); 00544 00545 // Delete the old cert since we're certainly done with it now 00546 if (d->pkcs) { 00547 delete d->pkcs; 00548 d->pkcs = NULL; 00549 } 00550 00551 if (!d->kssl) return; 00552 00553 // Look for a general certificate 00554 if (!forcePrompt) { 00555 certname = KSSLCertificateHome::getDefaultCertificateName(&aa); 00556 switch(aa) { 00557 case KSSLCertificateHome::AuthSend: 00558 send = true; prompt = false; 00559 break; 00560 case KSSLCertificateHome::AuthDont: 00561 send = false; prompt = false; 00562 certname = QString::null; 00563 break; 00564 case KSSLCertificateHome::AuthPrompt: 00565 send = false; prompt = true; 00566 break; 00567 default: 00568 break; 00569 } 00570 } 00571 00572 QString ourHost; 00573 if (!d->realHost.isEmpty()) { 00574 ourHost = d->realHost; 00575 } else { 00576 ourHost = d->host; 00577 } 00578 00579 // Look for a certificate on a per-host basis as an override 00580 QString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa); 00581 if (aa != KSSLCertificateHome::AuthNone) { // we must override 00582 switch (aa) { 00583 case KSSLCertificateHome::AuthSend: 00584 send = true; 00585 prompt = false; 00586 certname = tmpcn; 00587 break; 00588 case KSSLCertificateHome::AuthDont: 00589 send = false; 00590 prompt = false; 00591 certname = QString::null; 00592 break; 00593 case KSSLCertificateHome::AuthPrompt: 00594 send = false; 00595 prompt = true; 00596 certname = tmpcn; 00597 break; 00598 default: 00599 break; 00600 } 00601 } 00602 00603 // Finally, we allow the application to override anything. 00604 if (hasMetaData("ssl_demand_certificate")) { 00605 certname = metaData("ssl_demand_certificate"); 00606 if (!certname.isEmpty()) { 00607 forcePrompt = false; 00608 prompt = false; 00609 send = true; 00610 } 00611 } 00612 00613 if (certname.isEmpty() && !prompt && !forcePrompt) return; 00614 00615 // Ok, we're supposed to prompt the user.... 00616 if (prompt || forcePrompt) { 00617 QStringList certs = KSSLCertificateHome::getCertificateList(); 00618 00619 for (QStringList::Iterator it = certs.begin(); it != certs.end(); ++it) { 00620 KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it); 00621 if (pkcs && (!pkcs->getCertificate() || 00622 !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) { 00623 certs.remove(*it); 00624 } 00625 delete pkcs; 00626 } 00627 00628 if (certs.isEmpty()) return; // we had nothing else, and prompt failed 00629 00630 if (!d->dcc) { 00631 d->dcc = new DCOPClient; 00632 d->dcc->attach(); 00633 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 00634 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 00635 QStringList() ); 00636 } 00637 } 00638 00639 QByteArray data, retval; 00640 QCString rettype; 00641 QDataStream arg(data, IO_WriteOnly); 00642 arg << ourHost; 00643 arg << certs; 00644 arg << metaData("window-id").toInt(); 00645 bool rc = d->dcc->call("kio_uiserver", "UIServer", 00646 "showSSLCertDialog(QString, QStringList,int)", 00647 data, rettype, retval); 00648 00649 if (rc && rettype == "KSSLCertDlgRet") { 00650 QDataStream retStream(retval, IO_ReadOnly); 00651 KSSLCertDlgRet drc; 00652 retStream >> drc; 00653 if (drc.ok) { 00654 send = drc.send; 00655 save = drc.save; 00656 certname = drc.choice; 00657 } 00658 } 00659 } 00660 00661 // The user may have said to not send the certificate, 00662 // but to save the choice 00663 if (!send) { 00664 if (save) { 00665 KSSLCertificateHome::setDefaultCertificate(certname, ourHost, 00666 false, false); 00667 } 00668 return; 00669 } 00670 00671 // We're almost committed. If we can read the cert, we'll send it now. 00672 KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname); 00673 if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) { // We need the password 00674 KIO::AuthInfo ai; 00675 bool first = true; 00676 do { 00677 ai.prompt = i18n("Enter the certificate password:"); 00678 ai.caption = i18n("SSL Certificate Password"); 00679 ai.url.setProtocol("kssl"); 00680 ai.url.setHost(certname); 00681 ai.username = certname; 00682 ai.keepPassword = true; 00683 00684 bool showprompt; 00685 if (first) 00686 showprompt = !checkCachedAuthentication(ai); 00687 else 00688 showprompt = true; 00689 if (showprompt) { 00690 if (!openPassDlg(ai, first ? QString::null : 00691 i18n("Unable to open the certificate. Try a new password?"))) 00692 break; 00693 } 00694 00695 first = false; 00696 pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password); 00697 } while (!pkcs); 00698 00699 } 00700 00701 // If we could open the certificate, let's send it 00702 if (pkcs) { 00703 if (!d->kssl->setClientCertificate(pkcs)) { 00704 messageBox(Information, i18n("The procedure to set the " 00705 "client certificate for the session " 00706 "failed."), i18n("SSL")); 00707 delete pkcs; // we don't need this anymore 00708 pkcs = 0L; 00709 } else { 00710 kdDebug(7029) << "Client SSL certificate is being used." << endl; 00711 setMetaData("ssl_using_client_cert", "TRUE"); 00712 if (save) { 00713 KSSLCertificateHome::setDefaultCertificate(certname, ourHost, 00714 true, false); 00715 } 00716 } 00717 d->pkcs = pkcs; 00718 } 00719 } 00720 00721 00722 00723 bool TCPSlaveBase::usingTLS() const 00724 { 00725 return d->usingTLS; 00726 } 00727 00728 // ### remove this for KDE4 (misses const): 00729 bool TCPSlaveBase::usingTLS() 00730 { 00731 return d->usingTLS; 00732 } 00733 00734 00735 // Returns 0 for failed verification, -1 for rejected cert and 1 for ok 00736 int TCPSlaveBase::verifyCertificate() 00737 { 00738 int rc = 0; 00739 bool permacache = false; 00740 bool isChild = false; 00741 bool _IPmatchesCN = false; 00742 int result; 00743 bool doAddHost = false; 00744 QString ourHost; 00745 00746 if (!d->realHost.isEmpty()) 00747 ourHost = d->realHost; 00748 else ourHost = d->host; 00749 00750 QString theurl = QString(m_sServiceName)+"://"+ourHost+":"+QString::number(m_iPort); 00751 00752 if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE") 00753 d->militantSSL = false; 00754 else if (metaData("ssl_militant") == "TRUE") 00755 d->militantSSL = true; 00756 00757 if (!d->cc) d->cc = new KSSLCertificateCache; 00758 00759 KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate(); 00760 00761 KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer); 00762 00763 _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress(); 00764 if (!_IPmatchesCN) { 00765 #ifndef Q_WS_WIN //temporary 00766 KNetwork::KResolverResults res = KNetwork::KResolver::resolve(d->kssl->peerInfo().peerHost(), "80", KNetwork::KResolver::CanonName); 00767 if (!res.isEmpty()) { 00768 QString old = d->kssl->peerInfo().peerHost(); 00769 d->kssl->peerInfo().setPeerHost(res[0].canonicalName()); 00770 _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress(); 00771 if (!_IPmatchesCN) { 00772 d->kssl->peerInfo().setPeerHost(old); 00773 } 00774 } 00775 #endif 00776 if (!_IPmatchesCN && !d->militantSSL) { // force this if the user wants it 00777 if (d->cc->getHostList(pc).contains(ourHost)) { 00778 _IPmatchesCN = true; 00779 } 00780 } 00781 } 00782 00783 if (!_IPmatchesCN) { 00784 ksvl << KSSLCertificate::InvalidHost; 00785 } 00786 00787 KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok; 00788 if (!ksvl.isEmpty()) 00789 ksv = ksvl.first(); 00790 00791 /* Setting the various bits of meta-info that will be needed. */ 00792 setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher()); 00793 setMetaData("ssl_cipher_desc", 00794 d->kssl->connectionInfo().getCipherDescription()); 00795 setMetaData("ssl_cipher_version", 00796 d->kssl->connectionInfo().getCipherVersion()); 00797 setMetaData("ssl_cipher_used_bits", 00798 QString::number(d->kssl->connectionInfo().getCipherUsedBits())); 00799 setMetaData("ssl_cipher_bits", 00800 QString::number(d->kssl->connectionInfo().getCipherBits())); 00801 setMetaData("ssl_peer_ip", d->ip); 00802 if (!d->realHost.isEmpty()) { 00803 setMetaData("ssl_proxied", "true"); 00804 } 00805 00806 QString errorStr; 00807 for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin(); 00808 it != ksvl.end(); ++it) 00809 { 00810 errorStr += QString::number(*it)+":"; 00811 } 00812 setMetaData("ssl_cert_errors", errorStr); 00813 setMetaData("ssl_peer_certificate", pc.toString()); 00814 00815 if (pc.chain().isValid() && pc.chain().depth() > 1) { 00816 QString theChain; 00817 QPtrList<KSSLCertificate> chain = pc.chain().getChain(); 00818 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", QString::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 if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) { 00857 cp = KSSLCertificateCache::Prompt; 00858 // ksv = KSSLCertificate::Ok; 00859 } 00860 00861 // Precondition: cp is one of Reject, Accept or Prompt 00862 switch (cp) { 00863 case KSSLCertificateCache::Accept: 00864 rc = 1; 00865 setMetaData("ssl_action", "accept"); 00866 break; 00867 case KSSLCertificateCache::Reject: 00868 rc = -1; 00869 setMetaData("ssl_action", "reject"); 00870 break; 00871 case KSSLCertificateCache::Prompt: 00872 { 00873 do { 00874 if (ksv == KSSLCertificate::InvalidHost) { 00875 QString msg = i18n("The IP address of the host %1 " 00876 "does not match the one the " 00877 "certificate was issued to."); 00878 result = messageBox( WarningYesNoCancel, 00879 msg.arg(ourHost), 00880 i18n("Server Authentication"), 00881 i18n("&Details"), 00882 i18n("Co&ntinue") ); 00883 } else { 00884 QString msg = i18n("The server certificate failed the " 00885 "authenticity test (%1)."); 00886 result = messageBox( WarningYesNoCancel, 00887 msg.arg(ourHost), 00888 i18n("Server Authentication"), 00889 i18n("&Details"), 00890 i18n("Co&ntinue") ); 00891 } 00892 00893 if (result == KMessageBox::Yes) { 00894 if (!d->dcc) { 00895 d->dcc = new DCOPClient; 00896 d->dcc->attach(); 00897 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 00898 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 00899 QStringList() ); 00900 } 00901 00902 } 00903 QByteArray data, ignore; 00904 QCString ignoretype; 00905 QDataStream arg(data, IO_WriteOnly); 00906 arg << theurl << mOutgoingMetaData; 00907 arg << metaData("window-id").toInt(); 00908 d->dcc->call("kio_uiserver", "UIServer", 00909 "showSSLInfoDialog(QString,KIO::MetaData,int)", 00910 data, ignoretype, ignore); 00911 } 00912 } while (result == KMessageBox::Yes); 00913 00914 if (result == KMessageBox::No) { 00915 setMetaData("ssl_action", "accept"); 00916 rc = 1; 00917 cp = KSSLCertificateCache::Accept; 00918 doAddHost = true; 00919 result = messageBox( WarningYesNo, 00920 i18n("Would you like to accept this " 00921 "certificate forever without " 00922 "being prompted?"), 00923 i18n("Server Authentication"), 00924 i18n("&Forever"), 00925 i18n("&Current Sessions Only")); 00926 if (result == KMessageBox::Yes) 00927 permacache = true; 00928 else 00929 permacache = false; 00930 } else { 00931 setMetaData("ssl_action", "reject"); 00932 rc = -1; 00933 cp = KSSLCertificateCache::Prompt; 00934 } 00935 break; 00936 } 00937 default: 00938 kdDebug(7029) << "TCPSlaveBase/SSL error in cert code." 00939 << "Please report this to kfm-devel@kde.org." 00940 << endl; 00941 break; 00942 } 00943 } 00944 00945 00946 // - cache the results 00947 d->cc->addCertificate(pc, cp, permacache); 00948 if (doAddHost) d->cc->addHost(pc, ourHost); 00949 } else { // Child frame 00950 // - Read from cache and see if there is a policy for this 00951 KSSLCertificateCache::KSSLCertificatePolicy cp = 00952 d->cc->getPolicyByCertificate(pc); 00953 isChild = true; 00954 00955 // Check the cert and IP to make sure they're the same 00956 // as the parent frame 00957 bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") && 00958 pc.toString() == metaData("ssl_parent_cert")); 00959 00960 if (ksv == KSSLCertificate::Ok) { 00961 if (certAndIPTheSame) { // success 00962 rc = 1; 00963 setMetaData("ssl_action", "accept"); 00964 } else { 00965 /* 00966 if (d->militantSSL) { 00967 return -1; 00968 } 00969 result = messageBox(WarningYesNo, 00970 i18n("The certificate is valid but does not appear to have been assigned to this server. Do you wish to continue loading?"), 00971 i18n("Server Authentication")); 00972 if (result == KMessageBox::Yes) { // success 00973 rc = 1; 00974 setMetaData("ssl_action", "accept"); 00975 } else { // fail 00976 rc = -1; 00977 setMetaData("ssl_action", "reject"); 00978 } 00979 */ 00980 setMetaData("ssl_action", "accept"); 00981 rc = 1; // Let's accept this now. It's bad, but at least the user 00982 // will see potential attacks in KDE3 with the pseudo-lock 00983 // icon on the toolbar, and can investigate with the RMB 00984 } 00985 } else { 00986 if (d->militantSSL) { 00987 return -1; 00988 } 00989 00990 if (cp == KSSLCertificateCache::Accept) { 00991 if (certAndIPTheSame) { // success 00992 rc = 1; 00993 setMetaData("ssl_action", "accept"); 00994 } else { // fail 00995 result = messageBox(WarningYesNo, 00996 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?"), 00997 i18n("Server Authentication")); 00998 if (result == KMessageBox::Yes) { 00999 rc = 1; 01000 setMetaData("ssl_action", "accept"); 01001 d->cc->addHost(pc, ourHost); 01002 } else { 01003 rc = -1; 01004 setMetaData("ssl_action", "reject"); 01005 } 01006 } 01007 } else if (cp == KSSLCertificateCache::Reject) { // fail 01008 messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE Control Center."), 01009 i18n("Server Authentication")); 01010 rc = -1; 01011 setMetaData("ssl_action", "reject"); 01012 } else { 01013 do { 01014 QString msg = i18n("The server certificate failed the " 01015 "authenticity test (%1)."); 01016 result = messageBox(WarningYesNoCancel, 01017 msg.arg(ourHost), 01018 i18n("Server Authentication"), 01019 i18n("&Details"), 01020 i18n("Co&nnect")); 01021 if (result == KMessageBox::Yes) { 01022 if (!d->dcc) { 01023 d->dcc = new DCOPClient; 01024 d->dcc->attach(); 01025 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 01026 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 01027 QStringList() ); 01028 } 01029 } 01030 QByteArray data, ignore; 01031 QCString ignoretype; 01032 QDataStream arg(data, IO_WriteOnly); 01033 arg << theurl << mOutgoingMetaData; 01034 arg << metaData("window-id").toInt(); 01035 d->dcc->call("kio_uiserver", "UIServer", 01036 "showSSLInfoDialog(QString,KIO::MetaData,int)", 01037 data, ignoretype, ignore); 01038 } 01039 } while (result == KMessageBox::Yes); 01040 01041 if (result == KMessageBox::No) { 01042 setMetaData("ssl_action", "accept"); 01043 rc = 1; 01044 cp = KSSLCertificateCache::Accept; 01045 result = messageBox(WarningYesNo, 01046 i18n("Would you like to accept this " 01047 "certificate forever without " 01048 "being prompted?"), 01049 i18n("Server Authentication"), 01050 i18n("&Forever"), 01051 i18n("&Current Sessions Only")); 01052 permacache = (result == KMessageBox::Yes); 01053 d->cc->addCertificate(pc, cp, permacache); 01054 d->cc->addHost(pc, ourHost); 01055 } else { 01056 setMetaData("ssl_action", "reject"); 01057 rc = -1; 01058 cp = KSSLCertificateCache::Prompt; 01059 d->cc->addCertificate(pc, cp, permacache); 01060 } 01061 } 01062 } 01063 } 01064 01065 01066 if (rc == -1) { 01067 return rc; 01068 } 01069 01070 if (metaData("ssl_activate_warnings") == "TRUE") { 01071 // - entering SSL 01072 if (!isChild && metaData("ssl_was_in_use") == "FALSE" && 01073 d->kssl->settings()->warnOnEnter()) { 01074 int result; 01075 do { 01076 result = messageBox( i18n("You are about to " 01077 "enter secure mode. " 01078 "All transmissions " 01079 "will be encrypted " 01080 "unless otherwise " 01081 "noted.\nThis means " 01082 "that no third party " 01083 "will be able to " 01084 "easily observe your " 01085 "data in transit."), 01086 WarningYesNo, 01087 i18n("Security Information"), 01088 i18n("Display SSL " 01089 "&Information"), 01090 i18n("C&onnect"), 01091 "WarnOnEnterSSLMode" ); 01092 // Move this setting into KSSL instead 01093 KConfig *config = new KConfig("kioslaverc"); 01094 config->setGroup("Notification Messages"); 01095 01096 if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) { 01097 config->deleteEntry("WarnOnEnterSSLMode"); 01098 config->sync(); 01099 d->kssl->settings()->setWarnOnEnter(false); 01100 d->kssl->settings()->save(); 01101 } 01102 delete config; 01103 01104 if ( result == KMessageBox::Yes ) 01105 { 01106 if (!d->dcc) { 01107 d->dcc = new DCOPClient; 01108 d->dcc->attach(); 01109 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 01110 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 01111 QStringList() ); 01112 } 01113 } 01114 QByteArray data, ignore; 01115 QCString ignoretype; 01116 QDataStream arg(data, IO_WriteOnly); 01117 arg << theurl << mOutgoingMetaData; 01118 arg << metaData("window-id").toInt(); 01119 d->dcc->call("kio_uiserver", "UIServer", 01120 "showSSLInfoDialog(QString,KIO::MetaData,int)", 01121 data, ignoretype, ignore); 01122 } 01123 } while (result != KMessageBox::No); 01124 } 01125 01126 } // if ssl_activate_warnings 01127 01128 01129 kdDebug(7029) << "SSL connection information follows:" << endl 01130 << "+-----------------------------------------------" << endl 01131 << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl 01132 << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl 01133 << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl 01134 << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits() 01135 << " of " << d->kssl->connectionInfo().getCipherBits() 01136 << " bits used." << endl 01137 << "| PEER:" << endl 01138 << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl 01139 << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl 01140 << "| Validation: " << (int)ksv << endl 01141 << "| Certificate matches IP: " << _IPmatchesCN << endl 01142 << "+-----------------------------------------------" 01143 << endl; 01144 01145 // sendMetaData(); Do not call this function!! 01146 return rc; 01147 } 01148 01149 01150 bool TCPSlaveBase::isConnectionValid() 01151 { 01152 if ( m_iSock == -1 ) 01153 return false; 01154 01155 fd_set rdfs; 01156 FD_ZERO(&rdfs); 01157 FD_SET(m_iSock , &rdfs); 01158 01159 struct timeval tv; 01160 tv.tv_usec = 0; 01161 tv.tv_sec = 0; 01162 int retval; 01163 #ifdef Q_OS_UNIX 01164 do { 01165 retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv); 01166 if (wasKilled()) 01167 return false; // Beam us out of here 01168 } while ((retval == -1) && (errno == EAGAIN)); 01169 #else 01170 retval = -1; 01171 #endif 01172 // retval == -1 ==> Error 01173 // retval == 0 ==> Connection Idle 01174 // retval >= 1 ==> Connection Active 01175 //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: " 01176 // << retval << endl; 01177 01178 if (retval == -1) 01179 return false; 01180 01181 if (retval == 0) 01182 return true; 01183 01184 // Connection is active, check if it has closed. 01185 char buffer[100]; 01186 #ifdef Q_OS_UNIX 01187 do { 01188 retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK); 01189 01190 } while ((retval == -1) && (errno == EAGAIN)); 01191 #else 01192 retval = -1; 01193 #endif 01194 //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: " 01195 // << retval << endl; 01196 if (retval <= 0) 01197 return false; // Error or connection closed. 01198 01199 return true; // Connection still valid. 01200 } 01201 01202 01203 bool TCPSlaveBase::waitForResponse( int t ) 01204 { 01205 fd_set rd; 01206 struct timeval timeout; 01207 01208 if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl ) 01209 if (d->kssl->pending() > 0) 01210 return true; 01211 01212 FD_ZERO(&rd); 01213 FD_SET(m_iSock, &rd); 01214 01215 timeout.tv_usec = 0; 01216 timeout.tv_sec = t; 01217 time_t startTime; 01218 01219 int rc; 01220 int n = t; 01221 01222 reSelect: 01223 startTime = time(NULL); 01224 #ifdef Q_OS_UNIX 01225 rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout); 01226 #else 01227 rc = -1; 01228 #endif 01229 if (wasKilled()) 01230 return false; // We're dead. 01231 01232 if (rc == -1) 01233 return false; 01234 01235 if (FD_ISSET(m_iSock, &rd)) 01236 return true; 01237 01238 // Well it returned but it wasn't set. Let's see if it 01239 // returned too early (perhaps from an errant signal) and 01240 // start over with the remaining time 01241 int timeDone = time(NULL) - startTime; 01242 if (timeDone < n) 01243 { 01244 n -= timeDone; 01245 timeout.tv_sec = n; 01246 goto reSelect; 01247 } 01248 01249 return false; // Timed out! 01250 } 01251 01252 int TCPSlaveBase::connectResult() 01253 { 01254 return d->status; 01255 } 01256 01257 void TCPSlaveBase::setBlockConnection( bool b ) 01258 { 01259 d->block = b; 01260 } 01261 01262 void TCPSlaveBase::setConnectTimeout( int t ) 01263 { 01264 d->timeout = t; 01265 } 01266 01267 bool TCPSlaveBase::isSSLTunnelEnabled() 01268 { 01269 return d->useSSLTunneling; 01270 } 01271 01272 void TCPSlaveBase::setEnableSSLTunnel( bool enable ) 01273 { 01274 d->useSSLTunneling = enable; 01275 } 01276 01277 void TCPSlaveBase::setRealHost( const QString& realHost ) 01278 { 01279 d->realHost = realHost; 01280 } 01281 01282 bool TCPSlaveBase::doSSLHandShake( bool sendError ) 01283 { 01284 kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl; 01285 QString msgHost = d->host; 01286 01287 d->kssl->reInitialize(); 01288 01289 if (hasMetaData("ssl_session_id")) { 01290 KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id")); 01291 if (s) { 01292 d->kssl->setSession(s); 01293 delete s; 01294 } 01295 } 01296 certificatePrompt(); 01297 01298 if ( !d->realHost.isEmpty() ) 01299 { 01300 msgHost = d->realHost; 01301 } 01302 01303 kdDebug(7029) << "Setting real hostname: " << msgHost << endl; 01304 d->kssl->setPeerHost(msgHost); 01305 01306 d->status = d->kssl->connect(m_iSock); 01307 if (d->status < 0) 01308 { 01309 closeDescriptor(); 01310 if ( sendError ) 01311 error( ERR_COULD_NOT_CONNECT, msgHost); 01312 return false; 01313 } 01314 01315 setMetaData("ssl_session_id", d->kssl->session()->toString()); 01316 setMetaData("ssl_in_use", "TRUE"); 01317 01318 if (!d->kssl->reusingSession()) { 01319 int rc = verifyCertificate(); 01320 if ( rc != 1 ) { 01321 d->status = -1; 01322 closeDescriptor(); 01323 if ( sendError ) 01324 error( ERR_COULD_NOT_CONNECT, msgHost); 01325 return false; 01326 } 01327 } 01328 01329 d->needSSLHandShake = false; 01330 01331 d->savedMetaData = mOutgoingMetaData; 01332 return true; 01333 } 01334 01335 01336 bool TCPSlaveBase::userAborted() const 01337 { 01338 return d->userAborted; 01339 } 01340 01341 void TCPSlaveBase::virtual_hook( int id, void* data ) 01342 { SlaveBase::virtual_hook( id, data ); } 01343