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 if (d->session) { 00234 delete d->session; 00235 d->session = 0L; 00236 } 00237 00238 if (d->m_ssl) { 00239 d->kossl->SSL_shutdown(d->m_ssl); 00240 d->kossl->SSL_free(d->m_ssl); 00241 d->m_ssl = 0L; 00242 } 00243 00244 d->kossl->SSL_CTX_free(d->m_ctx); 00245 if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) { 00246 d->kossl->RAND_write_file(m_cfg->getEGDPath().latin1()); 00247 } 00248 00249 m_bInit = false; 00250 #endif 00251 } 00252 00253 00254 bool KSSL::reInitialize() { 00255 close(); 00256 return initialize(); 00257 } 00258 00259 // get the callback file - it's hidden away in here 00260 //#include "ksslcallback.c" 00261 00262 00263 bool KSSL::setVerificationLogic() { 00264 #if 0 00265 #ifdef KSSL_HAVE_SSL 00266 // SSL_set_verify_result(d->m_ssl, X509_V_OK); 00267 // SSL_CTX_set_verify(d->m_ctx, SSL_VERIFY_PEER, X509Callback); 00268 #endif 00269 #endif 00270 return true; 00271 } 00272 00273 00274 int KSSL::accept(int sock) { 00275 #ifdef KSSL_HAVE_SSL 00276 // kdDebug(7029) << "KSSL accept" << endl; 00277 int rc; 00278 if (!m_bInit) 00279 return -1; 00280 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00281 if (!d->m_ssl) 00282 return -1; 00283 00284 if (d->session) { 00285 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00286 { 00287 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00288 delete d->session; 00289 d->session = 0; 00290 } else if (1 == d->kossl->SSL_set_session(d->m_ssl, 00291 static_cast<SSL_SESSION*>(d->session->_session))) { 00292 kdDebug(7029) << "Session ID is being reused." << endl; 00293 } else { 00294 kdDebug(7029) << "Error attempting to reuse session." << endl; 00295 delete d->session; 00296 d->session = 0; 00297 } 00298 } 00299 00300 /* 00301 if (!setVerificationLogic()) { 00302 d->kossl->SSL_shutdown(d->m_ssl); 00303 d->kossl->SSL_free(d->m_ssl); 00304 d->m_ssl = 0; 00305 return -1; 00306 } 00307 */ 00308 00309 if (!d->lastInitTLS) 00310 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_NO_TLSv1); 00311 00312 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_ALL); 00313 00314 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00315 if (rc == 0) { 00316 d->kossl->SSL_shutdown(d->m_ssl); 00317 d->kossl->SSL_free(d->m_ssl); 00318 d->m_ssl = 0; 00319 return rc; 00320 } 00321 00322 rc = d->kossl->SSL_accept(d->m_ssl); 00323 if (rc == 1) { 00324 setConnectionInfo(); 00325 setPeerInfo(); 00326 kdDebug(7029) << "KSSL connected OK" << endl; 00327 } else { 00328 kdDebug(7029) << "KSSL accept failed - rc = " << rc << endl; 00329 kdDebug(7029) << " ERROR = " 00330 << d->kossl->SSL_get_error(d->m_ssl, rc) << endl; 00331 d->kossl->SSL_shutdown(d->m_ssl); 00332 d->kossl->SSL_free(d->m_ssl); 00333 d->m_ssl = 0; 00334 return -1; 00335 } 00336 00337 if (!d->kossl->SSL_session_reused(d->m_ssl)) { 00338 if (d->session) { 00339 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00340 delete d->session; 00341 d->session = 0L; 00342 } 00343 } 00344 00345 if (!d->session) { 00346 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00347 if (sess) { 00348 d->session = new KSSLSession; 00349 d->session->_session = sess; 00350 } 00351 } 00352 00353 return rc; 00354 #else 00355 return -1; 00356 #endif 00357 } 00358 00359 00360 int KSSL::connect(int sock) { 00361 #ifdef KSSL_HAVE_SSL 00362 // kdDebug(7029) << "KSSL connect" << endl; 00363 int rc; 00364 if (!m_bInit) 00365 return -1; 00366 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00367 if (!d->m_ssl) 00368 return -1; 00369 00370 if (d->session) { 00371 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00372 { 00373 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00374 delete d->session; 00375 d->session = 0; 00376 } else if (1 == d->kossl->SSL_set_session(d->m_ssl, 00377 static_cast<SSL_SESSION*>(d->session->_session))) { 00378 kdDebug(7029) << "Session ID is being reused." << endl; 00379 } else { 00380 kdDebug(7029) << "Error attempting to reuse session." << endl; 00381 delete d->session; 00382 d->session = 0; 00383 } 00384 } 00385 00386 /* 00387 if (!setVerificationLogic()) { 00388 d->kossl->SSL_shutdown(d->m_ssl); 00389 d->kossl->SSL_free(d->m_ssl); 00390 d->m_ssl = 0; 00391 return -1; 00392 } 00393 */ 00394 00395 if (!d->lastInitTLS) 00396 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_NO_TLSv1); 00397 00398 d->kossl->SSL_set_options(d->m_ssl, SSL_OP_ALL); 00399 00400 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00401 if (rc == 0) { 00402 d->kossl->SSL_shutdown(d->m_ssl); 00403 d->kossl->SSL_free(d->m_ssl); 00404 d->m_ssl = 0; 00405 return rc; 00406 } 00407 00408 connect_again: 00409 rc = d->kossl->SSL_connect(d->m_ssl); 00410 if (rc == 1) { 00411 setConnectionInfo(); 00412 setPeerInfo(); 00413 kdDebug(7029) << "KSSL connected OK" << endl; 00414 } else { 00415 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00416 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00417 // nonblocking - but we block anyways in connect() :) 00418 goto connect_again; 00419 } else { 00420 kdDebug(7029) << "KSSL connect failed - rc = " 00421 << rc << endl; 00422 kdDebug(7029) << " ERROR = " 00423 << err << endl; 00424 d->kossl->ERR_print_errors_fp(stderr); 00425 d->kossl->SSL_shutdown(d->m_ssl); 00426 d->kossl->SSL_free(d->m_ssl); 00427 d->m_ssl = 0; 00428 return -1; 00429 } 00430 } 00431 00432 if (!d->kossl->SSL_session_reused(d->m_ssl)) { 00433 if (d->session) { 00434 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00435 delete d->session; 00436 d->session = 0L; 00437 } 00438 } 00439 00440 if (!d->session) { 00441 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00442 if (sess) { 00443 d->session = new KSSLSession; 00444 d->session->_session = sess; 00445 } 00446 } 00447 00448 return rc; 00449 #else 00450 return -1; 00451 #endif 00452 } 00453 00454 00455 int KSSL::pending() { 00456 #ifdef KSSL_HAVE_SSL 00457 if (!m_bInit) 00458 return -1; 00459 return d->kossl->SSL_pending(d->m_ssl); 00460 #else 00461 return -1; 00462 #endif 00463 } 00464 00465 00466 int KSSL::peek(void *buf, int len) { 00467 #ifdef KSSL_HAVE_SSL 00468 if (!m_bInit) 00469 return -1; 00470 return d->kossl->SSL_peek(d->m_ssl, buf, len); 00471 #else 00472 return -1; 00473 #endif 00474 } 00475 00476 00477 int KSSL::read(void *buf, int len) { 00478 #ifdef KSSL_HAVE_SSL 00479 int rc = 0; 00480 00481 if (!m_bInit) 00482 return -1; 00483 00484 rc = d->kossl->SSL_read(d->m_ssl, (char *)buf, len); 00485 if (rc <= 0) { 00486 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00487 00488 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00489 return 0; 00490 } 00491 00492 kdDebug(7029) << "SSL READ ERROR: " << err << endl; 00493 if (err != SSL_ERROR_NONE && 00494 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) { 00495 rc = -1; // OpenSSL returns 0 on error too 00496 } 00497 00498 // else if (err == SSL_ERROR_ZERO_RETURN) 00499 // rc = 0; 00500 } 00501 return rc; 00502 #else 00503 return -1; 00504 #endif 00505 } 00506 00507 00508 int KSSL::write(const void *buf, int len) { 00509 #ifdef KSSL_HAVE_SSL 00510 if (!m_bInit) 00511 return -1; 00512 00513 int rc = d->kossl->SSL_write(d->m_ssl, (const char *)buf, len); 00514 if (rc <= 0) { // OpenSSL returns 0 on error too 00515 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00516 00517 if (err == SSL_ERROR_WANT_WRITE) { 00518 return 0; 00519 } 00520 00521 kdDebug(7029) << "SSL WRITE ERROR: " << err << endl; 00522 if (err != SSL_ERROR_NONE && 00523 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) 00524 rc = -1; 00525 } 00526 00527 return rc; 00528 #else 00529 return -1; 00530 #endif 00531 } 00532 00533 00534 bool KSSL::reconfig() { 00535 return reInitialize(); 00536 } 00537 00538 00539 void KSSL::setAutoReconfig(bool ar) { 00540 m_bAutoReconfig = ar; 00541 } 00542 00543 00544 bool KSSL::setSettings(KSSLSettings *settings) { 00545 delete m_cfg; 00546 m_cfg = settings; 00547 return reconfig(); 00548 } 00549 00550 00551 #ifdef KSSL_HAVE_SSL 00552 bool KSSL::m_bSSLWorks = true; 00553 #else 00554 bool KSSL::m_bSSLWorks = false; 00555 #endif 00556 00557 bool KSSL::doesSSLWork() { 00558 return m_bSSLWorks; 00559 } 00560 00561 00562 void KSSL::setConnectionInfo() { 00563 #ifdef KSSL_HAVE_SSL 00564 SSL_CIPHER *sc; 00565 char buf[1024]; 00566 00567 buf[0] = 0; // for safety. 00568 sc = d->kossl->SSL_get_current_cipher(d->m_ssl); 00569 if (!sc) { 00570 kdDebug(7029) << "KSSL get current cipher failed - we're probably gonna crash!" << endl; 00571 return; 00572 } 00573 00574 // set the number of bits, bits used 00575 m_ci.m_iCipherUsedBits = d->kossl->SSL_CIPHER_get_bits(sc, &(m_ci.m_iCipherBits)); 00576 // set the cipher version 00577 m_ci.m_cipherVersion = d->kossl->SSL_CIPHER_get_version(sc); 00578 // set the cipher name 00579 m_ci.m_cipherName = d->kossl->SSL_CIPHER_get_name(sc); 00580 // set the cipher description 00581 m_ci.m_cipherDescription = d->kossl->SSL_CIPHER_description(sc, buf, 1023); 00582 00583 #endif 00584 } 00585 00586 00587 void KSSL::setPeerInfo() { 00588 #ifdef KSSL_HAVE_SSL 00589 m_pi.setPeerHost(d->proxyPeer); 00590 m_pi.m_cert.setCert(d->kossl->SSL_get_peer_certificate(d->m_ssl)); 00591 STACK_OF(X509) *xs = d->kossl->SSL_get_peer_cert_chain(d->m_ssl); 00592 if (xs) 00593 xs = sk_X509_dup(xs); // Leak? 00594 m_pi.m_cert.setChain((void *)xs); 00595 #endif 00596 } 00597 00598 00599 KSSLConnectionInfo& KSSL::connectionInfo() { 00600 return m_ci; 00601 } 00602 00603 00604 // KDE 4: Make it const QString & 00605 void KSSL::setPeerHost(QString realHost) { 00606 d->proxyPeer = realHost; 00607 } 00608 00609 // deprecated 00610 void KSSL::setProxyUse(bool, QString, int, QString) { 00611 } 00612 00613 00614 KSSLPeerInfo& KSSL::peerInfo() { 00615 return m_pi; 00616 } 00617 00618 00619 bool KSSL::setClientCertificate(KSSLPKCS12 *pkcs) { 00620 #ifdef KSSL_HAVE_SSL 00621 if (!pkcs || !pkcs->getCertificate()) 00622 return false; 00623 00624 int rc; 00625 X509 *x = pkcs->getCertificate()->getCert(); 00626 EVP_PKEY *k = pkcs->getPrivateKey(); 00627 00628 if (!x || !k) return false; 00629 00630 if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient()) 00631 return false; 00632 00633 rc = d->kossl->SSL_CTX_use_certificate(d->m_ctx, x); 00634 if (rc <= 0) { 00635 kdDebug(7029) << "KSSL - SSL_CTX_use_certificate failed. rc = " << rc << endl; 00636 return false; 00637 } 00638 00639 rc = d->kossl->SSL_CTX_use_PrivateKey(d->m_ctx, k); 00640 if (rc <= 0) { 00641 kdDebug(7029) << "KSSL - SSL_CTX_use_PrivateKey failed. rc = " << rc << endl; 00642 return false; 00643 } 00644 00645 return true; 00646 #else 00647 return false; 00648 #endif 00649 } 00650 00651 #undef sk_dup 00652 00653 const KSSLSession* KSSL::session() const { 00654 return d->session; 00655 } 00656 00657 bool KSSL::reusingSession() const { 00658 #ifdef KSSL_HAVE_SSL 00659 return (d->m_ssl && d->kossl->SSL_session_reused(d->m_ssl)); 00660 #else 00661 return false; 00662 #endif 00663 } 00664 00665
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 Sat Jun 12 15:08:46 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003