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., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, 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 <tdelocale.h> 00052 #include <ksocks.h> 00053 00054 00055 class KSSLPrivate { 00056 public: 00057 KSSLPrivate() { 00058 lastInitTLS = false; 00059 kossl = KOpenSSLProxy::self(); 00060 session = 0L; 00061 } 00062 00063 ~KSSLPrivate() { 00064 delete session; 00065 session = 0L; 00066 } 00067 00068 bool lastInitTLS; 00069 KSSLCertificate::KSSLValidation m_cert_vfy_res; 00070 TQString proxyPeer; 00071 00072 #ifdef KSSL_HAVE_SSL 00073 SSL *m_ssl; 00074 SSL_CTX *m_ctx; 00075 SSL_METHOD *m_meth; 00076 #endif 00077 KSSLSession *session; 00078 KOSSL *kossl; 00079 }; 00080 00081 00082 KSSL::KSSL(bool init) { 00083 d = new KSSLPrivate; 00084 m_bInit = false; 00085 m_bAutoReconfig = true; 00086 m_cfg = new KSSLSettings(); 00087 #ifdef KSSL_HAVE_SSL 00088 d->m_ssl = 0L; 00089 #endif 00090 00091 if (init) 00092 initialize(); 00093 } 00094 00095 00096 KSSL::~KSSL() { 00097 close(); 00098 delete m_cfg; 00099 delete d; 00100 } 00101 00102 00103 int KSSL::seedWithEGD() { 00104 int rc = 0; 00105 #ifdef KSSL_HAVE_SSL 00106 if (m_cfg->useEGD() && !m_cfg->getEGDPath().isEmpty()) { 00107 rc = d->kossl->RAND_egd(m_cfg->getEGDPath().latin1()); 00108 if (rc < 0) 00109 kdDebug(7029) << "KSSL: Error seeding PRNG with the EGD." << endl; 00110 else kdDebug(7029) << "KSSL: PRNG was seeded with " << rc 00111 << " bytes from the EGD." << endl; 00112 } else if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) { 00113 rc = d->kossl->RAND_load_file(m_cfg->getEGDPath().latin1(), -1); 00114 if (rc < 0) 00115 kdDebug(7029) << "KSSL: Error seeding PRNG with the entropy file." << endl; 00116 else kdDebug(7029) << "KSSL: PRNG was seeded with " << rc 00117 << " bytes from the entropy file." << endl; 00118 } 00119 #endif 00120 return rc; 00121 } 00122 00123 00124 bool KSSL::TLSInit() { 00125 #ifdef KSSL_HAVE_SSL 00126 // kdDebug(7029) << "KSSL TLS initialize" << endl; 00127 if (m_bInit) 00128 return false; 00129 00130 if (m_bAutoReconfig) 00131 m_cfg->load(); 00132 00133 if (!m_cfg->tlsv1()) 00134 return false; 00135 00136 seedWithEGD(); 00137 d->m_meth = d->kossl->TLS_client_method(); 00138 d->lastInitTLS = true; 00139 00140 m_pi.reset(); 00141 00142 d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth); 00143 if (d->m_ctx == 0L) { 00144 return false; 00145 } 00146 00147 // set cipher list 00148 TQString clist = m_cfg->getCipherList(); 00149 //kdDebug(7029) << "Cipher list: " << clist << endl; 00150 if (!clist.isEmpty()) 00151 d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.ascii())); 00152 00153 m_bInit = true; 00154 return true; 00155 #else 00156 return false; 00157 #endif 00158 } 00159 00160 00161 bool KSSL::initialize() { 00162 #ifdef KSSL_HAVE_SSL 00163 kdDebug(7029) << "KSSL initialize" << endl; 00164 if (m_bInit) 00165 return false; 00166 00167 if (m_bAutoReconfig) 00168 m_cfg->load(); 00169 00170 seedWithEGD(); 00171 // FIXME: we should be able to force SSL off entirely. 00172 d->lastInitTLS = false; 00173 00174 m_pi.reset(); 00175 00176 if (m_cfg->tlsv1() || (m_cfg->sslv3() && m_cfg->sslv2())) { 00177 d->m_meth = d->kossl->TLS_client_method(); 00178 } 00179 else if (m_cfg->sslv3()) { 00180 d->m_meth = d->kossl->SSLv3_client_method(); 00181 } 00182 else if (m_cfg->sslv2()) { 00183 d->m_meth = d->kossl->SSLv2_client_method(); 00184 } 00185 00186 /* 00187 if (m_cfg->sslv2() && m_cfg->sslv3()) kdDebug(7029) << "Double method" << endl; 00188 else if (m_cfg->sslv2()) kdDebug(7029) << "SSL2 method" << endl; 00189 else if (m_cfg->sslv3()) kdDebug(7029) << "SSL3 method" << endl; 00190 */ 00191 00192 d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth); 00193 if (d->m_ctx == 0L) { 00194 return false; 00195 } 00196 00197 // set cipher list 00198 TQString clist = m_cfg->getCipherList(); 00199 kdDebug(7029) << "Cipher list: " << clist << endl; 00200 if (!clist.isEmpty()) 00201 d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.ascii())); 00202 00203 m_bInit = true; 00204 return true; 00205 #else 00206 return false; 00207 #endif 00208 } 00209 00210 00211 bool KSSL::takeSession(KSSLSession *session) { 00212 #ifdef KSSL_HAVE_SSL 00213 if (!session) { 00214 delete d->session; 00215 d->session = 0L; 00216 return true; 00217 } 00218 00219 // Take session reference 00220 d->session = new KSSLSession; 00221 d->session->_session = session->_session; 00222 session->_session = 0L; 00223 00224 return true; 00225 #else 00226 return false; 00227 #endif 00228 } 00229 00230 00231 void KSSL::close() { 00232 #ifdef KSSL_HAVE_SSL 00233 //kdDebug(7029) << "KSSL close" << endl; 00234 if (!m_bInit) 00235 return; 00236 00237 delete d->session; 00238 d->session = 0L; 00239 00240 if (d->m_ssl) { 00241 d->kossl->SSL_shutdown(d->m_ssl); 00242 d->kossl->SSL_free(d->m_ssl); 00243 d->m_ssl = 0L; 00244 } 00245 00246 d->kossl->SSL_CTX_free(d->m_ctx); 00247 if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) { 00248 d->kossl->RAND_write_file(m_cfg->getEGDPath().latin1()); 00249 } 00250 00251 m_bInit = false; 00252 #endif 00253 } 00254 00255 00256 bool KSSL::reInitialize() { 00257 close(); 00258 return initialize(); 00259 } 00260 00261 // get the callback file - it's hidden away in here 00262 //#include "ksslcallback.c" 00263 00264 00265 bool KSSL::setVerificationLogic() { 00266 #if 0 00267 #ifdef KSSL_HAVE_SSL 00268 // SSL_set_verify_result(d->m_ssl, X509_V_OK); 00269 // SSL_CTX_set_verify(d->m_ctx, SSL_VERIFY_PEER, X509Callback); 00270 #endif 00271 #endif 00272 return true; 00273 } 00274 00275 00276 int KSSL::accept(int sock) { 00277 #ifdef KSSL_HAVE_SSL 00278 // kdDebug(7029) << "KSSL accept" << endl; 00279 int rc; 00280 if (!m_bInit) 00281 return -1; 00282 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00283 if (!d->m_ssl) 00284 return -1; 00285 00286 if (d->session) { 00287 #if OPENSSL_VERSION_NUMBER < 0x10100000L 00288 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00289 { 00290 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00291 delete d->session; 00292 d->session = 0; 00293 } 00294 else 00295 #endif 00296 if (1 == d->kossl->SSL_set_session(d->m_ssl, 00297 static_cast<SSL_SESSION*>(d->session->_session))) { 00298 kdDebug(7029) << "Session ID is being reused." << endl; 00299 } else { 00300 kdDebug(7029) << "Error attempting to reuse session." << endl; 00301 delete d->session; 00302 d->session = 0; 00303 } 00304 } 00305 00306 /* 00307 if (!setVerificationLogic()) { 00308 d->kossl->SSL_shutdown(d->m_ssl); 00309 d->kossl->SSL_free(d->m_ssl); 00310 d->m_ssl = 0; 00311 return -1; 00312 } 00313 */ 00314 00315 int off = SSL_OP_ALL; 00316 if (!d->lastInitTLS && !m_cfg->tlsv1()) 00317 off |= SSL_OP_NO_TLSv1; 00318 if (!m_cfg->sslv3()) 00319 off |= SSL_OP_NO_SSLv3; 00320 if (!m_cfg->sslv2()) 00321 off |= SSL_OP_NO_SSLv2; 00322 00323 d->kossl->_SSL_set_options(d->m_ssl, off); 00324 00325 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00326 if (rc == 0) { 00327 d->kossl->SSL_shutdown(d->m_ssl); 00328 d->kossl->SSL_free(d->m_ssl); 00329 d->m_ssl = 0; 00330 return rc; 00331 } 00332 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME 00333 d->kossl->SSL_set_tlsext_host_name(d->m_ssl, d->proxyPeer.ascii()); 00334 #endif 00335 00336 rc = d->kossl->SSL_accept(d->m_ssl); 00337 if (rc == 1) { 00338 setConnectionInfo(); 00339 setPeerInfo(); 00340 kdDebug(7029) << "KSSL connected OK" << endl; 00341 } else { 00342 kdDebug(7029) << "KSSL accept failed - rc = " << rc << endl; 00343 kdDebug(7029) << " ERROR = " 00344 << d->kossl->SSL_get_error(d->m_ssl, rc) << endl; 00345 d->kossl->SSL_shutdown(d->m_ssl); 00346 d->kossl->SSL_free(d->m_ssl); 00347 d->m_ssl = 0; 00348 return -1; 00349 } 00350 00351 if (!d->kossl->_SSL_session_reused(d->m_ssl)) { 00352 if (d->session) { 00353 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00354 delete d->session; 00355 d->session = 0L; 00356 } 00357 } 00358 00359 if (!d->session) { 00360 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00361 if (sess) { 00362 d->session = new KSSLSession; 00363 d->session->_session = sess; 00364 } 00365 } 00366 00367 return rc; 00368 #else 00369 return -1; 00370 #endif 00371 } 00372 00373 00374 int KSSL::connect(int sock) { 00375 #ifdef KSSL_HAVE_SSL 00376 // kdDebug(7029) << "KSSL connect" << endl; 00377 int rc; 00378 if (!m_bInit) 00379 return -1; 00380 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00381 if (!d->m_ssl) 00382 return -1; 00383 00384 if (d->session) { 00385 #if OPENSSL_VERSION_NUMBER < 0x10100000L 00386 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00387 { 00388 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00389 delete d->session; 00390 d->session = 0; 00391 } 00392 else 00393 #endif 00394 if (1 == d->kossl->SSL_set_session(d->m_ssl, 00395 static_cast<SSL_SESSION*>(d->session->_session))) { 00396 kdDebug(7029) << "Session ID is being reused." << endl; 00397 } else { 00398 kdDebug(7029) << "Error attempting to reuse session." << endl; 00399 delete d->session; 00400 d->session = 0; 00401 } 00402 } 00403 00404 /* 00405 if (!setVerificationLogic()) { 00406 d->kossl->SSL_shutdown(d->m_ssl); 00407 d->kossl->SSL_free(d->m_ssl); 00408 d->m_ssl = 0; 00409 return -1; 00410 } 00411 */ 00412 00413 int off = SSL_OP_ALL; 00414 if (!d->lastInitTLS && !m_cfg->tlsv1()) 00415 off |= SSL_OP_NO_TLSv1; 00416 if (!m_cfg->sslv3()) 00417 off |= SSL_OP_NO_SSLv3; 00418 if (!m_cfg->sslv2()) 00419 off |= SSL_OP_NO_SSLv2; 00420 00421 d->kossl->_SSL_set_options(d->m_ssl, off); 00422 00423 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00424 if (rc == 0) { 00425 d->kossl->SSL_shutdown(d->m_ssl); 00426 d->kossl->SSL_free(d->m_ssl); 00427 d->m_ssl = 0; 00428 return rc; 00429 } 00430 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME 00431 d->kossl->SSL_set_tlsext_host_name(d->m_ssl, d->proxyPeer.ascii()); 00432 #endif 00433 00434 connect_again: 00435 rc = d->kossl->SSL_connect(d->m_ssl); 00436 if (rc == 1) { 00437 setConnectionInfo(); 00438 setPeerInfo(); 00439 kdDebug(7029) << "KSSL connected OK" << endl; 00440 } else { 00441 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00442 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00443 // nonblocking - but we block anyways in connect() :) 00444 goto connect_again; 00445 } else { 00446 kdDebug(7029) << "KSSL connect failed - rc = " 00447 << rc << endl; 00448 kdDebug(7029) << " ERROR = " 00449 << err << endl; 00450 d->kossl->ERR_print_errors_fp(stderr); 00451 d->kossl->SSL_shutdown(d->m_ssl); 00452 d->kossl->SSL_free(d->m_ssl); 00453 d->m_ssl = 0; 00454 return -1; 00455 } 00456 } 00457 00458 if (!d->kossl->_SSL_session_reused(d->m_ssl)) { 00459 if (d->session) { 00460 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00461 delete d->session; 00462 d->session = 0L; 00463 } 00464 } 00465 00466 if (!d->session) { 00467 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00468 if (sess) { 00469 d->session = new KSSLSession; 00470 d->session->_session = sess; 00471 } 00472 } 00473 00474 return rc; 00475 #else 00476 return -1; 00477 #endif 00478 } 00479 00480 00481 int KSSL::pending() { 00482 #ifdef KSSL_HAVE_SSL 00483 if (!m_bInit) 00484 return -1; 00485 return d->kossl->SSL_pending(d->m_ssl); 00486 #else 00487 return -1; 00488 #endif 00489 } 00490 00491 00492 int KSSL::peek(void *buf, int len) { 00493 #ifdef KSSL_HAVE_SSL 00494 if (!m_bInit) 00495 return -1; 00496 // FIXME: enhance to work the way read() does below, handling errors 00497 return d->kossl->SSL_peek(d->m_ssl, buf, len); 00498 #else 00499 return -1; 00500 #endif 00501 } 00502 00503 00504 int KSSL::read(void *buf, int len) { 00505 #ifdef KSSL_HAVE_SSL 00506 int rc = 0; 00507 int maxIters = 10; 00508 00509 if (!m_bInit) 00510 return -1; 00511 00512 read_again: 00513 rc = d->kossl->SSL_read(d->m_ssl, (char *)buf, len); 00514 if (rc <= 0) { 00515 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00516 00517 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00518 kdDebug(7029) << "SSL read() returning 0: " << err << endl; 00519 if (maxIters-- > 0) { 00520 ::usleep(20000); // 20ms sleep 00521 goto read_again; 00522 } 00523 return 0; 00524 } 00525 00526 kdDebug(7029) << "SSL READ ERROR: " << err << endl; 00527 if (err != SSL_ERROR_NONE && 00528 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) { 00529 rc = -1; // OpenSSL returns 0 on error too 00530 d->kossl->ERR_print_errors_fp(stderr); 00531 } 00532 00533 // else if (err == SSL_ERROR_ZERO_RETURN) 00534 // rc = 0; 00535 } 00536 return rc; 00537 #else 00538 return -1; 00539 #endif 00540 } 00541 00542 00543 int KSSL::write(const void *buf, int len) { 00544 #ifdef KSSL_HAVE_SSL 00545 if (!m_bInit) 00546 return -1; 00547 00548 write_again: 00549 int rc = d->kossl->SSL_write(d->m_ssl, (const char *)buf, len); 00550 if (rc <= 0) { // OpenSSL returns 0 on error too 00551 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00552 00553 if (err == SSL_ERROR_WANT_WRITE) { 00554 ::usleep(20000); // 20ms sleep 00555 goto write_again; 00556 } 00557 00558 kdDebug(7029) << "SSL WRITE ERROR: " << err << endl; 00559 if (err != SSL_ERROR_NONE && 00560 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) 00561 rc = -1; 00562 } 00563 00564 return rc; 00565 #else 00566 return -1; 00567 #endif 00568 } 00569 00570 00571 bool KSSL::reconfig() { 00572 return reInitialize(); 00573 } 00574 00575 00576 void KSSL::setAutoReconfig(bool ar) { 00577 m_bAutoReconfig = ar; 00578 } 00579 00580 00581 bool KSSL::setSettings(KSSLSettings *settings) { 00582 delete m_cfg; 00583 m_cfg = settings; 00584 return reconfig(); 00585 } 00586 00587 00588 #ifdef KSSL_HAVE_SSL 00589 bool KSSL::m_bSSLWorks = true; 00590 #else 00591 bool KSSL::m_bSSLWorks = false; 00592 #endif 00593 00594 bool KSSL::doesSSLWork() { 00595 return m_bSSLWorks; 00596 } 00597 00598 00599 void KSSL::setConnectionInfo() { 00600 #ifdef KSSL_HAVE_SSL 00601 SSL_CIPHER *sc; 00602 char buf[1024]; 00603 00604 buf[0] = 0; // for safety. 00605 sc = d->kossl->SSL_get_current_cipher(d->m_ssl); 00606 if (!sc) { 00607 kdDebug(7029) << "KSSL get current cipher failed - we're probably gonna crash!" << endl; 00608 return; 00609 } 00610 00611 // set the number of bits, bits used 00612 m_ci.m_iCipherUsedBits = d->kossl->SSL_CIPHER_get_bits(sc, &(m_ci.m_iCipherBits)); 00613 // set the cipher version 00614 m_ci.m_cipherVersion = d->kossl->SSL_CIPHER_get_version(sc); 00615 // set the cipher name 00616 m_ci.m_cipherName = d->kossl->SSL_CIPHER_get_name(sc); 00617 // set the cipher description 00618 m_ci.m_cipherDescription = d->kossl->SSL_CIPHER_description(sc, buf, 1023); 00619 00620 #endif 00621 } 00622 00623 00624 void KSSL::setPeerInfo() { 00625 #ifdef KSSL_HAVE_SSL 00626 m_pi.setPeerHost(d->proxyPeer); 00627 m_pi.m_cert.setCert(d->kossl->SSL_get_peer_certificate(d->m_ssl)); 00628 STACK_OF(X509) *xs = d->kossl->SSL_get_peer_cert_chain(d->m_ssl); 00629 if (xs) 00630 xs = reinterpret_cast<STACK_OF(X509)*>(d->kossl->OPENSSL_sk_dup(xs)); // Leak? 00631 m_pi.m_cert.setChain((void *)xs); 00632 #endif 00633 } 00634 00635 00636 KSSLConnectionInfo& KSSL::connectionInfo() { 00637 return m_ci; 00638 } 00639 00640 00641 // KDE 4: Make it const TQString & 00642 void KSSL::setPeerHost(TQString realHost) { 00643 d->proxyPeer = realHost; 00644 } 00645 00646 // deprecated 00647 void KSSL::setProxyUse(bool, TQString, int, TQString) { 00648 } 00649 00650 00651 KSSLPeerInfo& KSSL::peerInfo() { 00652 return m_pi; 00653 } 00654 00655 00656 bool KSSL::setClientCertificate(KSSLPKCS12 *pkcs) { 00657 #ifdef KSSL_HAVE_SSL 00658 if (!pkcs || !pkcs->getCertificate()) 00659 return false; 00660 00661 int rc; 00662 X509 *x = pkcs->getCertificate()->getCert(); 00663 EVP_PKEY *k = pkcs->getPrivateKey(); 00664 00665 if (!x || !k) return false; 00666 00667 if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient()) 00668 return false; 00669 00670 rc = d->kossl->SSL_CTX_use_certificate(d->m_ctx, x); 00671 if (rc <= 0) { 00672 kdDebug(7029) << "KSSL - SSL_CTX_use_certificate failed. rc = " << rc << endl; 00673 return false; 00674 } 00675 00676 rc = d->kossl->SSL_CTX_use_PrivateKey(d->m_ctx, k); 00677 if (rc <= 0) { 00678 kdDebug(7029) << "KSSL - SSL_CTX_use_PrivateKey failed. rc = " << rc << endl; 00679 return false; 00680 } 00681 00682 return true; 00683 #else 00684 return false; 00685 #endif 00686 } 00687 00688 const KSSLSession* KSSL::session() const { 00689 return d->session; 00690 } 00691 00692 bool KSSL::reusingSession() const { 00693 #ifdef KSSL_HAVE_SSL 00694 return (d->m_ssl && d->kossl->_SSL_session_reused(d->m_ssl)); 00695 #else 00696 return false; 00697 #endif 00698 } 00699