kio Library API Documentation

tcpslavebase.cpp

00001 /* 00002 * $Id: tcpslavebase.cpp,v 1.132 2003/10/23 09:04:34 rompf 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( WarningContinueCancel, 00303 i18n("You are about to leave secure " 00304 "mode. Transmissions will no " 00305 "longer be encrypted.\nThis " 00306 "means that a third party could " 00307 "observe your data in transit."), 00308 i18n("Security Information"), 00309 i18n("Continue Loading") ); 00310 if ( result == KMessageBox::Cancel ) { 00311 d->userAborted = true; 00312 return false; 00313 } 00314 } 00315 } 00316 00317 d->status = -1; 00318 d->host = host; 00319 d->needSSLHandShake = m_bIsSSL; 00320 p = port(_port); 00321 ks.setAddress(host, p); 00322 if ( d->timeout > -1 ) 00323 ks.setTimeout( d->timeout ); 00324 00325 if (ks.connect() < 0) 00326 { 00327 d->status = ks.status(); 00328 if ( sendError ) 00329 { 00330 if (d->status == IO_LookupError) 00331 error( ERR_UNKNOWN_HOST, host); 00332 else if ( d->status != -1 ) 00333 error( ERR_COULD_NOT_CONNECT, host); 00334 } 00335 return false; 00336 } 00337 00338 m_iSock = ks.fd(); 00339 00340 // store the IP for later 00341 const KSocketAddress *sa = ks.peerAddress(); 00342 if (sa) 00343 d->ip = sa->nodeName(); 00344 else 00345 d->ip = ""; 00346 00347 ks.release(); // KExtendedSocket no longer applicable 00348 00349 if ( d->block != ks.blockingMode() ) 00350 ks.setBlockingMode( d->block ); 00351 00352 m_iPort=p; 00353 00354 if (m_bIsSSL && !d->useSSLTunneling) { 00355 if ( !doSSLHandShake( sendError ) ) 00356 return false; 00357 } 00358 else 00359 setMetaData("ssl_in_use", "FALSE"); 00360 00361 // Since we want to use stdio on the socket, 00362 // we must fdopen it to get a file pointer, 00363 // if it fails, close everything up 00364 if ((fp = fdopen(m_iSock, "w+")) == 0) { 00365 closeDescriptor(); 00366 return false; 00367 } 00368 00369 return true; 00370 } 00371 00372 void TCPSlaveBase::closeDescriptor() 00373 { 00374 stopTLS(); 00375 if (fp) { 00376 fclose(fp); 00377 fp=0; 00378 m_iSock=-1; 00379 if (m_bIsSSL) 00380 d->kssl->close(); 00381 } 00382 if (m_iSock != -1) { 00383 close(m_iSock); 00384 m_iSock=-1; 00385 } 00386 d->ip = ""; 00387 d->host = ""; 00388 } 00389 00390 bool TCPSlaveBase::initializeSSL() 00391 { 00392 if (m_bIsSSL) { 00393 if (KSSL::doesSSLWork()) { 00394 d->kssl = new KSSL; 00395 return true; 00396 } 00397 } 00398 return false; 00399 } 00400 00401 void TCPSlaveBase::cleanSSL() 00402 { 00403 delete d->cc; 00404 00405 if (m_bIsSSL) { 00406 delete d->kssl; 00407 d->kssl = 0; 00408 } 00409 d->militantSSL = false; 00410 } 00411 00412 bool TCPSlaveBase::atEnd() 00413 { 00414 return feof(fp); 00415 } 00416 00417 int TCPSlaveBase::startTLS() 00418 { 00419 if (d->usingTLS || d->useSSLTunneling || m_bIsSSL || !KSSL::doesSSLWork()) 00420 return false; 00421 00422 d->kssl = new KSSL(false); 00423 if (!d->kssl->TLSInit()) { 00424 delete d->kssl; 00425 return -1; 00426 } 00427 00428 if ( !d->realHost.isEmpty() ) 00429 { 00430 kdDebug(7029) << "Setting real hostname: " << d->realHost << endl; 00431 d->kssl->setPeerHost(d->realHost); 00432 } else { 00433 kdDebug(7029) << "Setting real hostname: " << d->host << endl; 00434 d->kssl->setPeerHost(d->host); 00435 } 00436 00437 if (hasMetaData("ssl_session_id")) { 00438 KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id")); 00439 if (s) { 00440 d->kssl->setSession(s); 00441 delete s; 00442 } 00443 } 00444 certificatePrompt(); 00445 00446 int rc = d->kssl->connect(m_iSock); 00447 if (rc < 0) { 00448 delete d->kssl; 00449 return -2; 00450 } 00451 00452 setMetaData("ssl_session_id", d->kssl->session()->toString()); 00453 00454 d->usingTLS = true; 00455 setMetaData("ssl_in_use", "TRUE"); 00456 00457 if (!d->kssl->reusingSession()) { 00458 rc = verifyCertificate(); 00459 if (rc != 1) { 00460 setMetaData("ssl_in_use", "FALSE"); 00461 d->usingTLS = false; 00462 delete d->kssl; 00463 return -3; 00464 } 00465 } 00466 00467 d->savedMetaData = mOutgoingMetaData; 00468 return (d->usingTLS ? 1 : 0); 00469 } 00470 00471 00472 void TCPSlaveBase::stopTLS() 00473 { 00474 if (d->usingTLS) { 00475 delete d->kssl; 00476 d->usingTLS = false; 00477 setMetaData("ssl_in_use", "FALSE"); 00478 } 00479 } 00480 00481 00482 void TCPSlaveBase::setSSLMetaData() { 00483 if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL)) 00484 return; 00485 00486 mOutgoingMetaData = d->savedMetaData; 00487 } 00488 00489 00490 bool TCPSlaveBase::canUseTLS() 00491 { 00492 if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork()) 00493 return false; 00494 00495 KSSLSettings kss; 00496 return kss.tlsv1(); 00497 } 00498 00499 00500 void TCPSlaveBase::certificatePrompt() 00501 { 00502 QString certname; // the cert to use this session 00503 bool send = false, prompt = false, save = false, forcePrompt = false; 00504 KSSLCertificateHome::KSSLAuthAction aa; 00505 00506 setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed 00507 00508 if (metaData("ssl_no_client_cert") == "TRUE") return; 00509 forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE"); 00510 00511 // Delete the old cert since we're certainly done with it now 00512 if (d->pkcs) { 00513 delete d->pkcs; 00514 d->pkcs = NULL; 00515 } 00516 00517 if (!d->kssl) return; 00518 00519 // Look for a general certificate 00520 if (!forcePrompt) { 00521 certname = KSSLCertificateHome::getDefaultCertificateName(&aa); 00522 switch(aa) { 00523 case KSSLCertificateHome::AuthSend: 00524 send = true; prompt = false; 00525 break; 00526 case KSSLCertificateHome::AuthDont: 00527 send = false; prompt = false; 00528 certname = QString::null; 00529 break; 00530 case KSSLCertificateHome::AuthPrompt: 00531 send = false; prompt = true; 00532 break; 00533 default: 00534 break; 00535 } 00536 } 00537 00538 QString ourHost; 00539 if (!d->realHost.isEmpty()) { 00540 ourHost = d->realHost; 00541 } else { 00542 ourHost = d->host; 00543 } 00544 00545 // Look for a certificate on a per-host basis as an override 00546 QString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa); 00547 if (aa != KSSLCertificateHome::AuthNone) { // we must override 00548 switch (aa) { 00549 case KSSLCertificateHome::AuthSend: 00550 send = true; 00551 prompt = false; 00552 certname = tmpcn; 00553 break; 00554 case KSSLCertificateHome::AuthDont: 00555 send = false; 00556 prompt = false; 00557 certname = QString::null; 00558 break; 00559 case KSSLCertificateHome::AuthPrompt: 00560 send = false; 00561 prompt = true; 00562 certname = tmpcn; 00563 break; 00564 default: 00565 break; 00566 } 00567 } 00568 00569 // Finally, we allow the application to override anything. 00570 if (hasMetaData("ssl_demand_certificate")) { 00571 certname = metaData("ssl_demand_certificate"); 00572 if (!certname.isEmpty()) { 00573 forcePrompt = false; 00574 prompt = false; 00575 send = true; 00576 } 00577 } 00578 00579 if (certname.isEmpty() && !prompt && !forcePrompt) return; 00580 00581 // Ok, we're supposed to prompt the user.... 00582 if (prompt || forcePrompt) { 00583 QStringList certs = KSSLCertificateHome::getCertificateList(); 00584 00585 for (QStringList::Iterator it = certs.begin(); it != certs.end(); ++it) { 00586 KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it); 00587 if (pkcs && (!pkcs->getCertificate() || 00588 !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) { 00589 certs.remove(*it); 00590 } 00591 } 00592 00593 if (certs.isEmpty()) return; // we had nothing else, and prompt failed 00594 00595 if (!d->dcc) { 00596 d->dcc = new DCOPClient; 00597 d->dcc->attach(); 00598 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 00599 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 00600 QStringList() ); 00601 } 00602 } 00603 00604 QByteArray data, retval; 00605 QCString rettype; 00606 QDataStream arg(data, IO_WriteOnly); 00607 arg << ourHost; 00608 arg << certs; 00609 bool rc = d->dcc->call("kio_uiserver", "UIServer", 00610 "showSSLCertDialog(QString, QStringList)", 00611 data, rettype, retval); 00612 00613 if (rc && rettype == "KSSLCertDlgRet") { 00614 QDataStream retStream(retval, IO_ReadOnly); 00615 KSSLCertDlgRet drc; 00616 retStream >> drc; 00617 if (drc.ok) { 00618 send = drc.send; 00619 save = drc.save; 00620 certname = drc.choice; 00621 } 00622 } 00623 } 00624 00625 // The user may have said to not send the certificate, 00626 // but to save the choice 00627 if (!send) { 00628 if (save) { 00629 KSSLCertificateHome::setDefaultCertificate(certname, ourHost, 00630 false, false); 00631 } 00632 return; 00633 } 00634 00635 // We're almost committed. If we can read the cert, we'll send it now. 00636 KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname); 00637 if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) { // We need the password 00638 KIO::AuthInfo ai; 00639 bool showprompt = !checkCachedAuthentication(ai); 00640 do { 00641 QString pass; 00642 QByteArray authdata, authval; 00643 QCString rettype; 00644 QDataStream qds(authdata, IO_WriteOnly); 00645 ai.prompt = i18n("Enter the certificate password:"); 00646 ai.caption = i18n("SSL Certificate Password"); 00647 ai.setModified(true); 00648 ai.username = certname; 00649 ai.keepPassword = true; 00650 if (showprompt) { 00651 qds << ai; 00652 00653 if (!d->dcc) { 00654 d->dcc = new DCOPClient; 00655 d->dcc->attach(); 00656 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 00657 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 00658 QStringList() ); 00659 } 00660 } 00661 00662 bool rc = d->dcc->call("kio_uiserver", "UIServer", 00663 "openPassDlg(KIO::AuthInfo)", 00664 authdata, rettype, authval); 00665 if (!rc) { 00666 break; 00667 } 00668 if (rettype != "QByteArray") { 00669 continue; 00670 } 00671 00672 QDataStream qdret(authval, IO_ReadOnly); 00673 QByteArray authdecode; 00674 qdret >> authdecode; 00675 QDataStream qdtoo(authdecode, IO_ReadOnly); 00676 qdtoo >> ai; 00677 if (!ai.isModified()) { 00678 break; 00679 } 00680 } 00681 pass = ai.password; 00682 pkcs = KSSLCertificateHome::getCertificateByName(certname, pass); 00683 00684 if (!pkcs) { 00685 int rc = messageBox(WarningYesNo, i18n("Unable to open the " 00686 "certificate. Try a " 00687 "new password?"), 00688 i18n("SSL")); 00689 if (rc == KMessageBox::No) { 00690 break; 00691 } 00692 showprompt = true; 00693 } 00694 } while (!pkcs); 00695 if (pkcs) { 00696 cacheAuthentication(ai); 00697 } 00698 } 00699 00700 // If we could open the certificate, let's send it 00701 if (pkcs) { 00702 if (!d->kssl->setClientCertificate(pkcs)) { 00703 messageBox(Information, i18n("The procedure to set the " 00704 "client certificate for the session " 00705 "failed."), i18n("SSL")); 00706 delete pkcs; // we don't need this anymore 00707 pkcs = 0L; 00708 } else { 00709 kdDebug(7029) << "Client SSL certificate is being used." << endl; 00710 setMetaData("ssl_using_client_cert", "TRUE"); 00711 if (save) { 00712 KSSLCertificateHome::setDefaultCertificate(certname, ourHost, 00713 true, false); 00714 } 00715 } 00716 d->pkcs = pkcs; 00717 } 00718 } 00719 00720 00721 00722 bool TCPSlaveBase::usingTLS() const 00723 { 00724 return d->usingTLS; 00725 } 00726 00727 // ### remove this for KDE4 (misses const): 00728 bool TCPSlaveBase::usingTLS() 00729 { 00730 return d->usingTLS; 00731 } 00732 00733 00734 // Returns 0 for failed verification, -1 for rejected cert and 1 for ok 00735 int TCPSlaveBase::verifyCertificate() 00736 { 00737 int rc = 0; 00738 bool permacache = false; 00739 bool isChild = false; 00740 bool _IPmatchesCN = false; 00741 int result; 00742 bool doAddHost = false; 00743 QString ourHost; 00744 00745 if (!d->realHost.isEmpty()) 00746 ourHost = d->realHost; 00747 else ourHost = d->host; 00748 00749 QString theurl = QString(m_sServiceName)+"://"+ourHost+":"+QString::number(m_iPort); 00750 00751 if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE") 00752 d->militantSSL = false; 00753 else if (metaData("ssl_militant") == "TRUE") 00754 d->militantSSL = true; 00755 00756 if (!d->cc) d->cc = new KSSLCertificateCache; 00757 00758 KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate(); 00759 00760 KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer); 00761 00762 _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress(); 00763 if (!_IPmatchesCN && !d->militantSSL) { // force this if the user wants it 00764 if (d->cc->getHostList(pc).contains(ourHost)) 00765 _IPmatchesCN = true; 00766 } 00767 00768 if (!_IPmatchesCN) 00769 { 00770 ksvl << KSSLCertificate::InvalidHost; 00771 } 00772 00773 KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok; 00774 if (!ksvl.isEmpty()) 00775 ksv = ksvl.first(); 00776 00777 /* Setting the various bits of meta-info that will be needed. */ 00778 setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher()); 00779 setMetaData("ssl_cipher_desc", 00780 d->kssl->connectionInfo().getCipherDescription()); 00781 setMetaData("ssl_cipher_version", 00782 d->kssl->connectionInfo().getCipherVersion()); 00783 setMetaData("ssl_cipher_used_bits", 00784 QString::number(d->kssl->connectionInfo().getCipherUsedBits())); 00785 setMetaData("ssl_cipher_bits", 00786 QString::number(d->kssl->connectionInfo().getCipherBits())); 00787 setMetaData("ssl_peer_ip", d->ip); 00788 00789 QString errorStr; 00790 for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin(); 00791 it != ksvl.end(); ++it) 00792 { 00793 errorStr += QString::number(*it)+":"; 00794 } 00795 setMetaData("ssl_cert_errors", errorStr); 00796 setMetaData("ssl_peer_certificate", pc.toString()); 00797 00798 if (pc.chain().isValid() && pc.chain().depth() > 1) { 00799 QString theChain; 00800 QPtrList<KSSLCertificate> chain = pc.chain().getChain(); 00801 for (KSSLCertificate *c = chain.first(); c; c = chain.next()) { 00802 theChain += c->toString(); 00803 theChain += "\n"; 00804 } 00805 setMetaData("ssl_peer_chain", theChain); 00806 } else setMetaData("ssl_peer_chain", ""); 00807 00808 setMetaData("ssl_cert_state", QString::number(ksv)); 00809 00810 if (ksv == KSSLCertificate::Ok) { 00811 rc = 1; 00812 setMetaData("ssl_action", "accept"); 00813 } 00814 00815 kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl; 00816 if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") { 00817 // Since we're the parent, we need to teach the child. 00818 setMetaData("ssl_parent_ip", d->ip); 00819 setMetaData("ssl_parent_cert", pc.toString()); 00820 // - Read from cache and see if there is a policy for this 00821 KSSLCertificateCache::KSSLCertificatePolicy cp = 00822 d->cc->getPolicyByCertificate(pc); 00823 00824 // - validation code 00825 if (ksv != KSSLCertificate::Ok) { 00826 if (d->militantSSL) { 00827 return -1; 00828 } 00829 00830 if (cp == KSSLCertificateCache::Unknown || 00831 cp == KSSLCertificateCache::Ambiguous) { 00832 cp = KSSLCertificateCache::Prompt; 00833 } else { 00834 // A policy was already set so let's honor that. 00835 permacache = d->cc->isPermanent(pc); 00836 } 00837 00838 if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) { 00839 cp = KSSLCertificateCache::Prompt; 00840 // ksv = KSSLCertificate::Ok; 00841 } 00842 00843 // Precondition: cp is one of Reject, Accept or Prompt 00844 switch (cp) { 00845 case KSSLCertificateCache::Accept: 00846 rc = 1; 00847 setMetaData("ssl_action", "accept"); 00848 break; 00849 case KSSLCertificateCache::Reject: 00850 rc = -1; 00851 setMetaData("ssl_action", "reject"); 00852 break; 00853 case KSSLCertificateCache::Prompt: 00854 { 00855 do { 00856 if (ksv == KSSLCertificate::InvalidHost) { 00857 QString msg = i18n("The IP address of the host %1 " 00858 "does not match the one the " 00859 "certificate was issued to."); 00860 result = messageBox( WarningYesNoCancel, 00861 msg.arg(ourHost), 00862 i18n("Server Authentication"), 00863 i18n("&Details"), 00864 i18n("Co&ntinue") ); 00865 } else { 00866 QString msg = i18n("The server certificate failed the " 00867 "authenticity test (%1)."); 00868 result = messageBox( WarningYesNoCancel, 00869 msg.arg(ourHost), 00870 i18n("Server Authentication"), 00871 i18n("&Details"), 00872 i18n("Co&ntinue") ); 00873 } 00874 00875 if (result == KMessageBox::Yes) { 00876 if (!d->dcc) { 00877 d->dcc = new DCOPClient; 00878 d->dcc->attach(); 00879 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 00880 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 00881 QStringList() ); 00882 } 00883 00884 } 00885 QByteArray data, ignore; 00886 QCString ignoretype; 00887 QDataStream arg(data, IO_WriteOnly); 00888 arg << theurl << mOutgoingMetaData; 00889 d->dcc->call("kio_uiserver", "UIServer", 00890 "showSSLInfoDialog(QString,KIO::MetaData)", 00891 data, ignoretype, ignore); 00892 } 00893 } while (result == KMessageBox::Yes); 00894 00895 if (result == KMessageBox::No) { 00896 setMetaData("ssl_action", "accept"); 00897 rc = 1; 00898 cp = KSSLCertificateCache::Accept; 00899 doAddHost = true; 00900 result = messageBox( WarningYesNo, 00901 i18n("Would you like to accept this " 00902 "certificate forever without " 00903 "being prompted?"), 00904 i18n("Server Authentication"), 00905 i18n("&Forever"), 00906 i18n("&Current Sessions Only")); 00907 if (result == KMessageBox::Yes) 00908 permacache = true; 00909 else 00910 permacache = false; 00911 } else { 00912 setMetaData("ssl_action", "reject"); 00913 rc = -1; 00914 cp = KSSLCertificateCache::Prompt; 00915 } 00916 break; 00917 } 00918 default: 00919 kdDebug(7029) << "TCPSlaveBase/SSL error in cert code." 00920 << "Please report this to kfm-devel@kde.org." 00921 << endl; 00922 break; 00923 } 00924 } 00925 00926 00927 // - cache the results 00928 d->cc->addCertificate(pc, cp, permacache); 00929 if (doAddHost) d->cc->addHost(pc, ourHost); 00930 } else { // Child frame 00931 // - Read from cache and see if there is a policy for this 00932 KSSLCertificateCache::KSSLCertificatePolicy cp = 00933 d->cc->getPolicyByCertificate(pc); 00934 isChild = true; 00935 00936 // Check the cert and IP to make sure they're the same 00937 // as the parent frame 00938 bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") && 00939 pc.toString() == metaData("ssl_parent_cert")); 00940 00941 if (ksv == KSSLCertificate::Ok) { 00942 if (certAndIPTheSame) { // success 00943 rc = 1; 00944 setMetaData("ssl_action", "accept"); 00945 } else { 00946 /* 00947 if (d->militantSSL) { 00948 return -1; 00949 } 00950 result = messageBox(WarningYesNo, 00951 i18n("The certificate is valid but does not appear to have been assigned to this server. Do you wish to continue loading?"), 00952 i18n("Server Authentication")); 00953 if (result == KMessageBox::Yes) { // success 00954 rc = 1; 00955 setMetaData("ssl_action", "accept"); 00956 } else { // fail 00957 rc = -1; 00958 setMetaData("ssl_action", "reject"); 00959 } 00960 */ 00961 setMetaData("ssl_action", "accept"); 00962 rc = 1; // Let's accept this now. It's bad, but at least the user 00963 // will see potential attacks in KDE3 with the pseudo-lock 00964 // icon on the toolbar, and can investigate with the RMB 00965 } 00966 } else { 00967 if (d->militantSSL) { 00968 return -1; 00969 } 00970 00971 if (cp == KSSLCertificateCache::Accept) { 00972 if (certAndIPTheSame) { // success 00973 rc = 1; 00974 setMetaData("ssl_action", "accept"); 00975 } else { // fail 00976 result = messageBox(WarningYesNo, 00977 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?"), 00978 i18n("Server Authentication")); 00979 if (result == KMessageBox::Yes) { 00980 rc = 1; 00981 setMetaData("ssl_action", "accept"); 00982 d->cc->addHost(pc, ourHost); 00983 } else { 00984 rc = -1; 00985 setMetaData("ssl_action", "reject"); 00986 } 00987 } 00988 } else if (cp == KSSLCertificateCache::Reject) { // fail 00989 messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the KDE Control Center."), 00990 i18n("Server Authentication")); 00991 rc = -1; 00992 setMetaData("ssl_action", "reject"); 00993 } else { 00994 do { 00995 QString msg = i18n("The server certificate failed the " 00996 "authenticity test (%1)."); 00997 result = messageBox(WarningYesNoCancel, 00998 msg.arg(ourHost), 00999 i18n("Server Authentication"), 01000 i18n("&Details"), 01001 i18n("Co&ntinue")); 01002 if (result == KMessageBox::Yes) { 01003 if (!d->dcc) { 01004 d->dcc = new DCOPClient; 01005 d->dcc->attach(); 01006 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 01007 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 01008 QStringList() ); 01009 } 01010 } 01011 QByteArray data, ignore; 01012 QCString ignoretype; 01013 QDataStream arg(data, IO_WriteOnly); 01014 arg << theurl << mOutgoingMetaData; 01015 d->dcc->call("kio_uiserver", "UIServer", 01016 "showSSLInfoDialog(QString,KIO::MetaData)", 01017 data, ignoretype, ignore); 01018 } 01019 } while (result == KMessageBox::Yes); 01020 01021 if (result == KMessageBox::No) { 01022 setMetaData("ssl_action", "accept"); 01023 rc = 1; 01024 cp = KSSLCertificateCache::Accept; 01025 result = messageBox(WarningYesNo, 01026 i18n("Would you like to accept this " 01027 "certificate forever without " 01028 "being prompted?"), 01029 i18n("Server Authentication"), 01030 i18n("&Forever"), 01031 i18n("&Current Sessions Only")); 01032 permacache = (result == KMessageBox::Yes); 01033 d->cc->addCertificate(pc, cp, permacache); 01034 d->cc->addHost(pc, ourHost); 01035 } else { 01036 setMetaData("ssl_action", "reject"); 01037 rc = -1; 01038 cp = KSSLCertificateCache::Prompt; 01039 d->cc->addCertificate(pc, cp, permacache); 01040 } 01041 } 01042 } 01043 } 01044 01045 01046 if (rc == -1) { 01047 return rc; 01048 } 01049 01050 if (metaData("ssl_activate_warnings") == "TRUE") { 01051 // - entering SSL 01052 if (!isChild && metaData("ssl_was_in_use") == "FALSE" && 01053 d->kssl->settings()->warnOnEnter()) { 01054 int result; 01055 do { 01056 result = messageBox( WarningYesNo, i18n("You are about to " 01057 "enter secure mode. " 01058 "All transmissions " 01059 "will be encrypted " 01060 "unless otherwise " 01061 "noted.\nThis means " 01062 "that no third party " 01063 "will be able to " 01064 "easily observe your " 01065 "data in transit."), 01066 i18n("Security Information"), 01067 i18n("Display SSL " 01068 "Information"), 01069 i18n("Continue") ); 01070 if ( result == KMessageBox::Yes ) 01071 { 01072 if (!d->dcc) { 01073 d->dcc = new DCOPClient; 01074 d->dcc->attach(); 01075 if (!d->dcc->isApplicationRegistered("kio_uiserver")) { 01076 KApplication::startServiceByDesktopPath("kio_uiserver.desktop", 01077 QStringList() ); 01078 } 01079 } 01080 QByteArray data, ignore; 01081 QCString ignoretype; 01082 QDataStream arg(data, IO_WriteOnly); 01083 arg << theurl << mOutgoingMetaData; 01084 d->dcc->call("kio_uiserver", "UIServer", 01085 "showSSLInfoDialog(QString,KIO::MetaData)", 01086 data, ignoretype, ignore); 01087 } 01088 } while (result != KMessageBox::No); 01089 } 01090 01091 } // if ssl_activate_warnings 01092 01093 01094 kdDebug(7029) << "SSL connection information follows:" << endl 01095 << "+-----------------------------------------------" << endl 01096 << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl 01097 << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl 01098 << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl 01099 << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits() 01100 << " of " << d->kssl->connectionInfo().getCipherBits() 01101 << " bits used." << endl 01102 << "| PEER:" << endl 01103 << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl 01104 << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl 01105 << "| Validation: " << (int)ksv << endl 01106 << "| Certificate matches IP: " << _IPmatchesCN << endl 01107 << "+-----------------------------------------------" 01108 << endl; 01109 01110 // sendMetaData(); Do not call this function!! 01111 return rc; 01112 } 01113 01114 01115 bool TCPSlaveBase::isConnectionValid() 01116 { 01117 if ( m_iSock == -1 ) 01118 return false; 01119 01120 fd_set rdfs; 01121 FD_ZERO(&rdfs); 01122 FD_SET(m_iSock , &rdfs); 01123 01124 struct timeval tv; 01125 tv.tv_usec = 0; 01126 tv.tv_sec = 0; 01127 int retval; 01128 do { 01129 retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv); 01130 if (wasKilled()) 01131 return false; // Beam us out of here 01132 } while ((retval == -1) && (errno == EAGAIN)); 01133 // retval == -1 ==> Error 01134 // retval == 0 ==> Connection Idle 01135 // retval >= 1 ==> Connection Active 01136 //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: " 01137 // << retval << endl; 01138 01139 if (retval == -1) 01140 return false; 01141 01142 if (retval == 0) 01143 return true; 01144 01145 // Connection is active, check if it has closed. 01146 char buffer[100]; 01147 do { 01148 retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK); 01149 01150 } while ((retval == -1) && (errno == EAGAIN)); 01151 //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: " 01152 // << retval << endl; 01153 if (retval <= 0) 01154 return false; // Error or connection closed. 01155 01156 return true; // Connection still valid. 01157 } 01158 01159 01160 bool TCPSlaveBase::waitForResponse( int t ) 01161 { 01162 fd_set rd; 01163 struct timeval timeout; 01164 01165 if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl ) 01166 if (d->kssl->pending() > 0) 01167 return true; 01168 01169 FD_ZERO(&rd); 01170 FD_SET(m_iSock, &rd); 01171 01172 timeout.tv_usec = 0; 01173 timeout.tv_sec = t; 01174 time_t startTime; 01175 01176 int rc; 01177 int n = t; 01178 01179 reSelect: 01180 startTime = time(NULL); 01181 rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout); 01182 if (wasKilled()) 01183 return false; // We're dead. 01184 01185 if (rc == -1) 01186 return false; 01187 01188 if (FD_ISSET(m_iSock, &rd)) 01189 return true; 01190 01191 // Well it returned but it wasn't set. Let's see if it 01192 // returned too early (perhaps from an errant signal) and 01193 // start over with the remaining time 01194 int timeDone = time(NULL) - startTime; 01195 if (timeDone < n) 01196 { 01197 n -= timeDone; 01198 timeout.tv_sec = n; 01199 goto reSelect; 01200 } 01201 01202 return false; // Timed out! 01203 } 01204 01205 int TCPSlaveBase::connectResult() 01206 { 01207 return d->status; 01208 } 01209 01210 void TCPSlaveBase::setBlockConnection( bool b ) 01211 { 01212 d->block = b; 01213 } 01214 01215 void TCPSlaveBase::setConnectTimeout( int t ) 01216 { 01217 d->timeout = t; 01218 } 01219 01220 bool TCPSlaveBase::isSSLTunnelEnabled() 01221 { 01222 return d->useSSLTunneling; 01223 } 01224 01225 void TCPSlaveBase::setEnableSSLTunnel( bool enable ) 01226 { 01227 d->useSSLTunneling = enable; 01228 } 01229 01230 void TCPSlaveBase::setRealHost( const QString& realHost ) 01231 { 01232 d->realHost = realHost; 01233 } 01234 01235 bool TCPSlaveBase::doSSLHandShake( bool sendError ) 01236 { 01237 kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl; 01238 QString msgHost = d->host; 01239 01240 d->kssl->reInitialize(); 01241 01242 if (hasMetaData("ssl_session_id")) { 01243 KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id")); 01244 if (s) { 01245 d->kssl->setSession(s); 01246 delete s; 01247 } 01248 } 01249 certificatePrompt(); 01250 01251 if ( !d->realHost.isEmpty() ) 01252 { 01253 msgHost = d->realHost; 01254 } 01255 01256 kdDebug(7029) << "Setting real hostname: " << msgHost << endl; 01257 d->kssl->setPeerHost(msgHost); 01258 01259 d->status = d->kssl->connect(m_iSock); 01260 if (d->status < 0) 01261 { 01262 closeDescriptor(); 01263 if ( sendError ) 01264 error( ERR_COULD_NOT_CONNECT, msgHost); 01265 return false; 01266 } 01267 01268 setMetaData("ssl_session_id", d->kssl->session()->toString()); 01269 setMetaData("ssl_in_use", "TRUE"); 01270 01271 if (!d->kssl->reusingSession()) { 01272 int rc = verifyCertificate(); 01273 if ( rc != 1 ) { 01274 d->status = -1; 01275 closeDescriptor(); 01276 if ( sendError ) 01277 error( ERR_COULD_NOT_CONNECT, msgHost); 01278 return false; 01279 } 01280 } 01281 01282 d->needSSLHandShake = false; 01283 01284 d->savedMetaData = mOutgoingMetaData; 01285 return true; 01286 } 01287 01288 01289 bool TCPSlaveBase::userAborted() const 01290 { 01291 return d->userAborted; 01292 } 01293 01294 void TCPSlaveBase::virtual_hook( int id, void* data ) 01295 { SlaveBase::virtual_hook( id, data ); } 01296
KDE Logo
This file is part of the documentation for kio Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 8 11:15:01 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003