kio Library API Documentation

kssl.cc

00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 2000-2003 George Staikos <staikos@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 * Boston, MA 02111-1307, USA. 00019 */ 00020 00021 #ifdef HAVE_CONFIG_H 00022 #include <config.h> 00023 #endif 00024 00025 // this hack provided by Malte Starostik to avoid glibc/openssl bug 00026 // on some systems 00027 #ifdef KSSL_HAVE_SSL 00028 #include <unistd.h> 00029 #include <netinet/in.h> 00030 #include <sys/socket.h> 00031 #define crypt _openssl_crypt 00032 #include <openssl/ssl.h> 00033 #include <openssl/x509.h> 00034 #include <openssl/x509v3.h> 00035 #include <openssl/pem.h> 00036 #include <openssl/rand.h> 00037 #undef crypt 00038 #endif 00039 00040 #include "kssl.h" 00041 00042 #include <kdebug.h> 00043 #include <kstandarddirs.h> 00044 #include <ksock.h> 00045 #include <ksockaddr.h> 00046 00047 #include <kopenssl.h> 00048 #include <ksslx509v3.h> 00049 #include <ksslpkcs12.h> 00050 #include <ksslsession.h> 00051 #include <klocale.h> 00052 #include <ksocks.h> 00053 00054 #define sk_dup d->kossl->sk_dup 00055 00056 class KSSLPrivate { 00057 public: 00058 KSSLPrivate() { 00059 lastInitTLS = false; 00060 kossl = KOpenSSLProxy::self(); 00061 session = 0L; 00062 } 00063 00064 ~KSSLPrivate() { 00065 delete session; 00066 session = 0L; 00067 } 00068 00069 bool lastInitTLS; 00070 KSSLCertificate::KSSLValidation m_cert_vfy_res; 00071 QString proxyPeer; 00072 00073 #ifdef KSSL_HAVE_SSL 00074 SSL *m_ssl; 00075 SSL_CTX *m_ctx; 00076 SSL_METHOD *m_meth; 00077 #endif 00078 KSSLSession *session; 00079 KOSSL *kossl; 00080 }; 00081 00082 00083 KSSL::KSSL(bool init) { 00084 d = new KSSLPrivate; 00085 m_bInit = false; 00086 m_bAutoReconfig = true; 00087 m_cfg = new KSSLSettings(); 00088 #ifdef KSSL_HAVE_SSL 00089 d->m_ssl = 0L; 00090 #endif 00091 00092 if (init) 00093 initialize(); 00094 } 00095 00096 00097 KSSL::~KSSL() { 00098 close(); 00099 delete m_cfg; 00100 delete d; 00101 } 00102 00103 00104 int KSSL::seedWithEGD() { 00105 int rc = 0; 00106 #ifdef KSSL_HAVE_SSL 00107 if (m_cfg->useEGD() && !m_cfg->getEGDPath().isEmpty()) { 00108 rc = d->kossl->RAND_egd(m_cfg->getEGDPath().latin1()); 00109 if (rc < 0) 00110 kdDebug(7029) << "KSSL: Error seeding PRNG with the EGD." << endl; 00111 else kdDebug(7029) << "KSSL: PRNG was seeded with " << rc 00112 << " bytes from the EGD." << endl; 00113 } else if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) { 00114 rc = d->kossl->RAND_load_file(m_cfg->getEGDPath().latin1(), -1); 00115 if (rc < 0) 00116 kdDebug(7029) << "KSSL: Error seeding PRNG with the entropy file." << endl; 00117 else kdDebug(7029) << "KSSL: PRNG was seeded with " << rc 00118 << " bytes from the entropy file." << endl; 00119 } 00120 #endif 00121 return rc; 00122 } 00123 00124 00125 bool KSSL::TLSInit() { 00126 #ifdef KSSL_HAVE_SSL 00127 // kdDebug(7029) << "KSSL TLS initialize" << endl; 00128 if (m_bInit) 00129 return false; 00130 00131 if (m_bAutoReconfig) 00132 m_cfg->load(); 00133 00134 if (!m_cfg->tlsv1()) 00135 return false; 00136 00137 seedWithEGD(); 00138 d->m_meth = d->kossl->TLSv1_client_method(); 00139 d->lastInitTLS = true; 00140 00141 d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth); 00142 if (d->m_ctx == 0L) { 00143 return false; 00144 } 00145 00146 // set cipher list 00147 QString clist = m_cfg->getCipherList(); 00148 //kdDebug(7029) << "Cipher list: " << clist << endl; 00149 if (!clist.isEmpty()) 00150 d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.ascii())); 00151 00152 m_bInit = true; 00153 return true; 00154 #else 00155 return false; 00156 #endif 00157 } 00158 00159 00160 bool KSSL::initialize() { 00161 #ifdef KSSL_HAVE_SSL 00162 kdDebug(7029) << "KSSL initialize" << endl; 00163 if (m_bInit) 00164 return false; 00165 00166 if (m_bAutoReconfig) 00167 m_cfg->load(); 00168 00169 seedWithEGD(); 00170 // FIXME: we should be able to force SSL off entirely. 00171 d->lastInitTLS = false; 00172 00173 m_pi.reset(); 00174 00175 if (m_cfg->sslv2() && !m_cfg->sslv3()) 00176 d->m_meth = d->kossl->SSLv2_client_method(); 00177 else if (m_cfg->sslv3() && !m_cfg->sslv2()) 00178 d->m_meth = d->kossl->SSLv3_client_method(); 00179 else d->m_meth = d->kossl->SSLv23_client_method(); 00180 00181 /* 00182 if (m_cfg->sslv2() && m_cfg->sslv3()) kdDebug(7029) << "Double method" << endl; 00183 else if (m_cfg->sslv2()) kdDebug(7029) << "SSL2 method" << endl; 00184 else if (m_cfg->sslv3()) kdDebug(7029) << "SSL3 method" << endl; 00185 */ 00186 00187 d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth); 00188 if (d->m_ctx == 0L) { 00189 return false; 00190 } 00191 00192 // set cipher list 00193 QString clist = m_cfg->getCipherList(); 00194 kdDebug(7029) << "Cipher list: " << clist << endl; 00195 if (!clist.isEmpty()) 00196 d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.ascii())); 00197 00198 m_bInit = true; 00199 return true; 00200 #else 00201 return false; 00202 #endif 00203 } 00204 00205 00206 bool KSSL::setSession(const KSSLSession *session) { 00207 #ifdef KSSL_HAVE_SSL 00208 if (!session) { 00209 delete d->session; 00210 d->session = 0L; 00211 return true; 00212 } 00213 00214 // Obtain a reference by incrementing the reference count. Yuck. 00215 static_cast<SSL_SESSION*>(session->_session)->references++; 00216 00217 d->session = new KSSLSession; 00218 d->session->_session = session->_session; 00219 00220 return true; 00221 #else 00222 return false; 00223 #endif 00224 } 00225 00226 00227 void KSSL::close() { 00228 #ifdef KSSL_HAVE_SSL 00229 //kdDebug(7029) << "KSSL close" << endl; 00230 if (!m_bInit) 00231 return; 00232 00233 delete d->session; 00234 d->session = 0L; 00235 00236 if (d->m_ssl) { 00237 d->kossl->SSL_shutdown(d->m_ssl); 00238 d->kossl->SSL_free(d->m_ssl); 00239 d->m_ssl = 0L; 00240 } 00241 00242 d->kossl->SSL_CTX_free(d->m_ctx); 00243 if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) { 00244 d->kossl->RAND_write_file(m_cfg->getEGDPath().latin1()); 00245 } 00246 00247 m_bInit = false; 00248 #endif 00249 } 00250 00251 00252 bool KSSL::reInitialize() { 00253 close(); 00254 return initialize(); 00255 } 00256 00257 // get the callback file - it's hidden away in here 00258 //#include "ksslcallback.c" 00259 00260 00261 bool KSSL::setVerificationLogic() { 00262 #if 0 00263 #ifdef KSSL_HAVE_SSL 00264 // SSL_set_verify_result(d->m_ssl, X509_V_OK); 00265 // SSL_CTX_set_verify(d->m_ctx, SSL_VERIFY_PEER, X509Callback); 00266 #endif 00267 #endif 00268 return true; 00269 } 00270 00271 00272 int KSSL::accept(int sock) { 00273 #ifdef KSSL_HAVE_SSL 00274 // kdDebug(7029) << "KSSL accept" << endl; 00275 int rc; 00276 if (!m_bInit) 00277 return -1; 00278 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00279 if (!d->m_ssl) 00280 return -1; 00281 00282 if (d->session) { 00283 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00284 { 00285 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00286 delete d->session; 00287 d->session = 0; 00288 } else if (1 == d->kossl->SSL_set_session(d->m_ssl, 00289 static_cast<SSL_SESSION*>(d->session->_session))) { 00290 kdDebug(7029) << "Session ID is being reused." << endl; 00291 } else { 00292 kdDebug(7029) << "Error attempting to reuse session." << endl; 00293 delete d->session; 00294 d->session = 0; 00295 } 00296 } 00297 00298 /* 00299 if (!setVerificationLogic()) { 00300 d->kossl->SSL_shutdown(d->m_ssl); 00301 d->kossl->SSL_free(d->m_ssl); 00302 d->m_ssl = 0; 00303 return -1; 00304 } 00305 */ 00306 00307 if (!d->lastInitTLS) 00308 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_NO_TLSv1); 00309 00310 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_ALL); 00311 00312 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00313 if (rc == 0) { 00314 d->kossl->SSL_shutdown(d->m_ssl); 00315 d->kossl->SSL_free(d->m_ssl); 00316 d->m_ssl = 0; 00317 return rc; 00318 } 00319 00320 rc = d->kossl->SSL_accept(d->m_ssl); 00321 if (rc == 1) { 00322 setConnectionInfo(); 00323 setPeerInfo(); 00324 kdDebug(7029) << "KSSL connected OK" << endl; 00325 } else { 00326 kdDebug(7029) << "KSSL accept failed - rc = " << rc << endl; 00327 kdDebug(7029) << " ERROR = " 00328 << d->kossl->SSL_get_error(d->m_ssl, rc) << endl; 00329 d->kossl->SSL_shutdown(d->m_ssl); 00330 d->kossl->SSL_free(d->m_ssl); 00331 d->m_ssl = 0; 00332 return -1; 00333 } 00334 00335 if (!d->kossl->SSL_session_reused(d->m_ssl)) { 00336 if (d->session) { 00337 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00338 delete d->session; 00339 d->session = 0L; 00340 } 00341 } 00342 00343 if (!d->session) { 00344 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00345 if (sess) { 00346 d->session = new KSSLSession; 00347 d->session->_session = sess; 00348 } 00349 } 00350 00351 return rc; 00352 #else 00353 return -1; 00354 #endif 00355 } 00356 00357 00358 int KSSL::connect(int sock) { 00359 #ifdef KSSL_HAVE_SSL 00360 // kdDebug(7029) << "KSSL connect" << endl; 00361 int rc; 00362 if (!m_bInit) 00363 return -1; 00364 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00365 if (!d->m_ssl) 00366 return -1; 00367 00368 if (d->session) { 00369 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00370 { 00371 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00372 delete d->session; 00373 d->session = 0; 00374 } else if (1 == d->kossl->SSL_set_session(d->m_ssl, 00375 static_cast<SSL_SESSION*>(d->session->_session))) { 00376 kdDebug(7029) << "Session ID is being reused." << endl; 00377 } else { 00378 kdDebug(7029) << "Error attempting to reuse session." << endl; 00379 delete d->session; 00380 d->session = 0; 00381 } 00382 } 00383 00384 /* 00385 if (!setVerificationLogic()) { 00386 d->kossl->SSL_shutdown(d->m_ssl); 00387 d->kossl->SSL_free(d->m_ssl); 00388 d->m_ssl = 0; 00389 return -1; 00390 } 00391 */ 00392 00393 if (!d->lastInitTLS) 00394 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_NO_TLSv1); 00395 00396 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_ALL); 00397 00398 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00399 if (rc == 0) { 00400 d->kossl->SSL_shutdown(d->m_ssl); 00401 d->kossl->SSL_free(d->m_ssl); 00402 d->m_ssl = 0; 00403 return rc; 00404 } 00405 00406 connect_again: 00407 rc = d->kossl->SSL_connect(d->m_ssl); 00408 if (rc == 1) { 00409 setConnectionInfo(); 00410 setPeerInfo(); 00411 kdDebug(7029) << "KSSL connected OK" << endl; 00412 } else { 00413 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00414 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00415 // nonblocking - but we block anyways in connect() :) 00416 goto connect_again; 00417 } else { 00418 kdDebug(7029) << "KSSL connect failed - rc = " 00419 << rc << endl; 00420 kdDebug(7029) << " ERROR = " 00421 << err << endl; 00422 d->kossl->ERR_print_errors_fp(stderr); 00423 d->kossl->SSL_shutdown(d->m_ssl); 00424 d->kossl->SSL_free(d->m_ssl); 00425 d->m_ssl = 0; 00426 return -1; 00427 } 00428 } 00429 00430 if (!d->kossl->SSL_session_reused(d->m_ssl)) { 00431 if (d->session) { 00432 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00433 delete d->session; 00434 d->session = 0L; 00435 } 00436 } 00437 00438 if (!d->session) { 00439 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00440 if (sess) { 00441 d->session = new KSSLSession; 00442 d->session->_session = sess; 00443 } 00444 } 00445 00446 return rc; 00447 #else 00448 return -1; 00449 #endif 00450 } 00451 00452 00453 int KSSL::pending() { 00454 #ifdef KSSL_HAVE_SSL 00455 if (!m_bInit) 00456 return -1; 00457 return d->kossl->SSL_pending(d->m_ssl); 00458 #else 00459 return -1; 00460 #endif 00461 } 00462 00463 00464 int KSSL::peek(void *buf, int len) { 00465 #ifdef KSSL_HAVE_SSL 00466 if (!m_bInit) 00467 return -1; 00468 // FIXME: enhance to work the way read() does below, handling errors 00469 return d->kossl->SSL_peek(d->m_ssl, buf, len); 00470 #else 00471 return -1; 00472 #endif 00473 } 00474 00475 00476 int KSSL::read(void *buf, int len) { 00477 #ifdef KSSL_HAVE_SSL 00478 int rc = 0; 00479 int maxIters = 10; 00480 00481 if (!m_bInit) 00482 return -1; 00483 00484 read_again: 00485 rc = d->kossl->SSL_read(d->m_ssl, (char *)buf, len); 00486 if (rc <= 0) { 00487 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00488 00489 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00490 kdDebug(7029) << "SSL read() returning 0: " << err << endl; 00491 if (maxIters-- > 0) { 00492 ::usleep(20000); // 20ms sleep 00493 goto read_again; 00494 } 00495 return 0; 00496 } 00497 00498 kdDebug(7029) << "SSL READ ERROR: " << err << endl; 00499 if (err != SSL_ERROR_NONE && 00500 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) { 00501 rc = -1; // OpenSSL returns 0 on error too 00502 } 00503 00504 // else if (err == SSL_ERROR_ZERO_RETURN) 00505 // rc = 0; 00506 } 00507 return rc; 00508 #else 00509 return -1; 00510 #endif 00511 } 00512 00513 00514 int KSSL::write(const void *buf, int len) { 00515 #ifdef KSSL_HAVE_SSL 00516 if (!m_bInit) 00517 return -1; 00518 00519 write_again: 00520 int rc = d->kossl->SSL_write(d->m_ssl, (const char *)buf, len); 00521 if (rc <= 0) { // OpenSSL returns 0 on error too 00522 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00523 00524 if (err == SSL_ERROR_WANT_WRITE) { 00525 ::usleep(20000); // 20ms sleep 00526 goto write_again; 00527 } 00528 00529 kdDebug(7029) << "SSL WRITE ERROR: " << err << endl; 00530 if (err != SSL_ERROR_NONE && 00531 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) 00532 rc = -1; 00533 } 00534 00535 return rc; 00536 #else 00537 return -1; 00538 #endif 00539 } 00540 00541 00542 bool KSSL::reconfig() { 00543 return reInitialize(); 00544 } 00545 00546 00547 void KSSL::setAutoReconfig(bool ar) { 00548 m_bAutoReconfig = ar; 00549 } 00550 00551 00552 bool KSSL::setSettings(KSSLSettings *settings) { 00553 delete m_cfg; 00554 m_cfg = settings; 00555 return reconfig(); 00556 } 00557 00558 00559 #ifdef KSSL_HAVE_SSL 00560 bool KSSL::m_bSSLWorks = true; 00561 #else 00562 bool KSSL::m_bSSLWorks = false; 00563 #endif 00564 00565 bool KSSL::doesSSLWork() { 00566 return m_bSSLWorks; 00567 } 00568 00569 00570 void KSSL::setConnectionInfo() { 00571 #ifdef KSSL_HAVE_SSL 00572 SSL_CIPHER *sc; 00573 char buf[1024]; 00574 00575 buf[0] = 0; // for safety. 00576 sc = d->kossl->SSL_get_current_cipher(d->m_ssl); 00577 if (!sc) { 00578 kdDebug(7029) << "KSSL get current cipher failed - we're probably gonna crash!" << endl; 00579 return; 00580 } 00581 00582 // set the number of bits, bits used 00583 m_ci.m_iCipherUsedBits = d->kossl->SSL_CIPHER_get_bits(sc, &(m_ci.m_iCipherBits)); 00584 // set the cipher version 00585 m_ci.m_cipherVersion = d->kossl->SSL_CIPHER_get_version(sc); 00586 // set the cipher name 00587 m_ci.m_cipherName = d->kossl->SSL_CIPHER_get_name(sc); 00588 // set the cipher description 00589 m_ci.m_cipherDescription = d->kossl->SSL_CIPHER_description(sc, buf, 1023); 00590 00591 #endif 00592 } 00593 00594 00595 void KSSL::setPeerInfo() { 00596 #ifdef KSSL_HAVE_SSL 00597 m_pi.setPeerHost(d->proxyPeer); 00598 m_pi.m_cert.setCert(d->kossl->SSL_get_peer_certificate(d->m_ssl)); 00599 STACK_OF(X509) *xs = d->kossl->SSL_get_peer_cert_chain(d->m_ssl); 00600 if (xs) 00601 xs = sk_X509_dup(xs); // Leak? 00602 m_pi.m_cert.setChain((void *)xs); 00603 #endif 00604 } 00605 00606 00607 KSSLConnectionInfo& KSSL::connectionInfo() { 00608 return m_ci; 00609 } 00610 00611 00612 // KDE 4: Make it const QString & 00613 void KSSL::setPeerHost(QString realHost) { 00614 d->proxyPeer = realHost; 00615 } 00616 00617 // deprecated 00618 void KSSL::setProxyUse(bool, QString, int, QString) { 00619 } 00620 00621 00622 KSSLPeerInfo& KSSL::peerInfo() { 00623 return m_pi; 00624 } 00625 00626 00627 bool KSSL::setClientCertificate(KSSLPKCS12 *pkcs) { 00628 #ifdef KSSL_HAVE_SSL 00629 if (!pkcs || !pkcs->getCertificate()) 00630 return false; 00631 00632 int rc; 00633 X509 *x = pkcs->getCertificate()->getCert(); 00634 EVP_PKEY *k = pkcs->getPrivateKey(); 00635 00636 if (!x || !k) return false; 00637 00638 if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient()) 00639 return false; 00640 00641 rc = d->kossl->SSL_CTX_use_certificate(d->m_ctx, x); 00642 if (rc <= 0) { 00643 kdDebug(7029) << "KSSL - SSL_CTX_use_certificate failed. rc = " << rc << endl; 00644 return false; 00645 } 00646 00647 rc = d->kossl->SSL_CTX_use_PrivateKey(d->m_ctx, k); 00648 if (rc <= 0) { 00649 kdDebug(7029) << "KSSL - SSL_CTX_use_PrivateKey failed. rc = " << rc << endl; 00650 return false; 00651 } 00652 00653 return true; 00654 #else 00655 return false; 00656 #endif 00657 } 00658 00659 #undef sk_dup 00660 00661 const KSSLSession* KSSL::session() const { 00662 return d->session; 00663 } 00664 00665 bool KSSL::reusingSession() const { 00666 #ifdef KSSL_HAVE_SSL 00667 return (d->m_ssl && d->kossl->SSL_session_reused(d->m_ssl)); 00668 #else 00669 return false; 00670 #endif 00671 } 00672 00673
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 Thu Apr 14 00:20:29 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003