kio Library API Documentation

tcpslavebase.cpp

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