kio Library API Documentation

tcpslavebase.cpp

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