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 <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 m_pi.reset(); 00142 00143 d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth); 00144 if (d->m_ctx == 0L) { 00145 return false; 00146 } 00147 00148 // set cipher list 00149 QString clist = m_cfg->getCipherList(); 00150 //kdDebug(7029) << "Cipher list: " << clist << endl; 00151 if (!clist.isEmpty()) 00152 d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.ascii())); 00153 00154 m_bInit = true; 00155 return true; 00156 #else 00157 return false; 00158 #endif 00159 } 00160 00161 00162 bool KSSL::initialize() { 00163 #ifdef KSSL_HAVE_SSL 00164 kdDebug(7029) << "KSSL initialize" << endl; 00165 if (m_bInit) 00166 return false; 00167 00168 if (m_bAutoReconfig) 00169 m_cfg->load(); 00170 00171 seedWithEGD(); 00172 // FIXME: we should be able to force SSL off entirely. 00173 d->lastInitTLS = false; 00174 00175 m_pi.reset(); 00176 00177 if (!m_cfg->tlsv1() && !m_cfg->sslv3() && m_cfg->sslv2()) 00178 d->m_meth = d->kossl->SSLv2_client_method(); 00179 else if (m_cfg->tlsv1() && !m_cfg->sslv3() && !m_cfg->sslv2()) 00180 d->m_meth = d->kossl->TLSv1_client_method(); 00181 else if (!m_cfg->tlsv1() && m_cfg->sslv3() && !m_cfg->sslv2()) 00182 d->m_meth = d->kossl->SSLv3_client_method(); 00183 else d->m_meth = d->kossl->SSLv23_client_method(); 00184 00185 /* 00186 if (m_cfg->sslv2() && m_cfg->sslv3()) kdDebug(7029) << "Double method" << endl; 00187 else if (m_cfg->sslv2()) kdDebug(7029) << "SSL2 method" << endl; 00188 else if (m_cfg->sslv3()) kdDebug(7029) << "SSL3 method" << endl; 00189 */ 00190 00191 d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth); 00192 if (d->m_ctx == 0L) { 00193 return false; 00194 } 00195 00196 // set cipher list 00197 QString clist = m_cfg->getCipherList(); 00198 kdDebug(7029) << "Cipher list: " << clist << endl; 00199 if (!clist.isEmpty()) 00200 d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.ascii())); 00201 00202 m_bInit = true; 00203 return true; 00204 #else 00205 return false; 00206 #endif 00207 } 00208 00209 00210 bool KSSL::setSession(const KSSLSession *session) { 00211 #ifdef KSSL_HAVE_SSL 00212 if (!session) { 00213 delete d->session; 00214 d->session = 0L; 00215 return true; 00216 } 00217 00218 // Obtain a reference by incrementing the reference count. Yuck. 00219 static_cast<SSL_SESSION*>(session->_session)->references++; 00220 00221 d->session = new KSSLSession; 00222 d->session->_session = session->_session; 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 (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00288 { 00289 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00290 delete d->session; 00291 d->session = 0; 00292 } else if (1 == d->kossl->SSL_set_session(d->m_ssl, 00293 static_cast<SSL_SESSION*>(d->session->_session))) { 00294 kdDebug(7029) << "Session ID is being reused." << endl; 00295 } else { 00296 kdDebug(7029) << "Error attempting to reuse session." << endl; 00297 delete d->session; 00298 d->session = 0; 00299 } 00300 } 00301 00302 /* 00303 if (!setVerificationLogic()) { 00304 d->kossl->SSL_shutdown(d->m_ssl); 00305 d->kossl->SSL_free(d->m_ssl); 00306 d->m_ssl = 0; 00307 return -1; 00308 } 00309 */ 00310 00311 int off = SSL_OP_ALL; 00312 if (!d->lastInitTLS && !m_cfg->tlsv1()) 00313 off |= SSL_OP_NO_TLSv1; 00314 if (!m_cfg->sslv3()) 00315 off |= SSL_OP_NO_SSLv3; 00316 if (!m_cfg->sslv2()) 00317 off |= SSL_OP_NO_SSLv2; 00318 00319 d->kossl->SSL_set_options(d->m_ssl, off); 00320 00321 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00322 if (rc == 0) { 00323 d->kossl->SSL_shutdown(d->m_ssl); 00324 d->kossl->SSL_free(d->m_ssl); 00325 d->m_ssl = 0; 00326 return rc; 00327 } 00328 00329 rc = d->kossl->SSL_accept(d->m_ssl); 00330 if (rc == 1) { 00331 setConnectionInfo(); 00332 setPeerInfo(); 00333 kdDebug(7029) << "KSSL connected OK" << endl; 00334 } else { 00335 kdDebug(7029) << "KSSL accept failed - rc = " << rc << endl; 00336 kdDebug(7029) << " ERROR = " 00337 << d->kossl->SSL_get_error(d->m_ssl, rc) << endl; 00338 d->kossl->SSL_shutdown(d->m_ssl); 00339 d->kossl->SSL_free(d->m_ssl); 00340 d->m_ssl = 0; 00341 return -1; 00342 } 00343 00344 if (!d->kossl->SSL_session_reused(d->m_ssl)) { 00345 if (d->session) { 00346 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00347 delete d->session; 00348 d->session = 0L; 00349 } 00350 } 00351 00352 if (!d->session) { 00353 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00354 if (sess) { 00355 d->session = new KSSLSession; 00356 d->session->_session = sess; 00357 } 00358 } 00359 00360 return rc; 00361 #else 00362 return -1; 00363 #endif 00364 } 00365 00366 00367 int KSSL::connect(int sock) { 00368 #ifdef KSSL_HAVE_SSL 00369 // kdDebug(7029) << "KSSL connect" << endl; 00370 int rc; 00371 if (!m_bInit) 00372 return -1; 00373 d->m_ssl = d->kossl->SSL_new(d->m_ctx); 00374 if (!d->m_ssl) 00375 return -1; 00376 00377 if (d->session) { 00378 if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0) 00379 { 00380 kdDebug(7029) << "Can't reuse session, no certificate." << endl; 00381 delete d->session; 00382 d->session = 0; 00383 } else if (1 == d->kossl->SSL_set_session(d->m_ssl, 00384 static_cast<SSL_SESSION*>(d->session->_session))) { 00385 kdDebug(7029) << "Session ID is being reused." << endl; 00386 } else { 00387 kdDebug(7029) << "Error attempting to reuse session." << endl; 00388 delete d->session; 00389 d->session = 0; 00390 } 00391 } 00392 00393 /* 00394 if (!setVerificationLogic()) { 00395 d->kossl->SSL_shutdown(d->m_ssl); 00396 d->kossl->SSL_free(d->m_ssl); 00397 d->m_ssl = 0; 00398 return -1; 00399 } 00400 */ 00401 00402 int off = SSL_OP_ALL; 00403 if (!d->lastInitTLS && !m_cfg->tlsv1()) 00404 off |= SSL_OP_NO_TLSv1; 00405 if (!m_cfg->sslv3()) 00406 off |= SSL_OP_NO_SSLv3; 00407 if (!m_cfg->sslv2()) 00408 off |= SSL_OP_NO_SSLv2; 00409 00410 d->kossl->SSL_set_options(d->m_ssl, off); 00411 00412 rc = d->kossl->SSL_set_fd(d->m_ssl, sock); 00413 if (rc == 0) { 00414 d->kossl->SSL_shutdown(d->m_ssl); 00415 d->kossl->SSL_free(d->m_ssl); 00416 d->m_ssl = 0; 00417 return rc; 00418 } 00419 00420 connect_again: 00421 rc = d->kossl->SSL_connect(d->m_ssl); 00422 if (rc == 1) { 00423 setConnectionInfo(); 00424 setPeerInfo(); 00425 kdDebug(7029) << "KSSL connected OK" << endl; 00426 } else { 00427 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00428 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00429 // nonblocking - but we block anyways in connect() :) 00430 goto connect_again; 00431 } else { 00432 kdDebug(7029) << "KSSL connect failed - rc = " 00433 << rc << endl; 00434 kdDebug(7029) << " ERROR = " 00435 << err << endl; 00436 d->kossl->ERR_print_errors_fp(stderr); 00437 d->kossl->SSL_shutdown(d->m_ssl); 00438 d->kossl->SSL_free(d->m_ssl); 00439 d->m_ssl = 0; 00440 return -1; 00441 } 00442 } 00443 00444 if (!d->kossl->SSL_session_reused(d->m_ssl)) { 00445 if (d->session) { 00446 kdDebug(7029) << "Session reuse failed. New session used instead." << endl; 00447 delete d->session; 00448 d->session = 0L; 00449 } 00450 } 00451 00452 if (!d->session) { 00453 SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl); 00454 if (sess) { 00455 d->session = new KSSLSession; 00456 d->session->_session = sess; 00457 } 00458 } 00459 00460 return rc; 00461 #else 00462 return -1; 00463 #endif 00464 } 00465 00466 00467 int KSSL::pending() { 00468 #ifdef KSSL_HAVE_SSL 00469 if (!m_bInit) 00470 return -1; 00471 return d->kossl->SSL_pending(d->m_ssl); 00472 #else 00473 return -1; 00474 #endif 00475 } 00476 00477 00478 int KSSL::peek(void *buf, int len) { 00479 #ifdef KSSL_HAVE_SSL 00480 if (!m_bInit) 00481 return -1; 00482 // FIXME: enhance to work the way read() does below, handling errors 00483 return d->kossl->SSL_peek(d->m_ssl, buf, len); 00484 #else 00485 return -1; 00486 #endif 00487 } 00488 00489 00490 int KSSL::read(void *buf, int len) { 00491 #ifdef KSSL_HAVE_SSL 00492 int rc = 0; 00493 int maxIters = 10; 00494 00495 if (!m_bInit) 00496 return -1; 00497 00498 read_again: 00499 rc = d->kossl->SSL_read(d->m_ssl, (char *)buf, len); 00500 if (rc <= 0) { 00501 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00502 00503 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 00504 kdDebug(7029) << "SSL read() returning 0: " << err << endl; 00505 if (maxIters-- > 0) { 00506 ::usleep(20000); // 20ms sleep 00507 goto read_again; 00508 } 00509 return 0; 00510 } 00511 00512 kdDebug(7029) << "SSL READ ERROR: " << err << endl; 00513 if (err != SSL_ERROR_NONE && 00514 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) { 00515 rc = -1; // OpenSSL returns 0 on error too 00516 d->kossl->ERR_print_errors_fp(stderr); 00517 } 00518 00519 // else if (err == SSL_ERROR_ZERO_RETURN) 00520 // rc = 0; 00521 } 00522 return rc; 00523 #else 00524 return -1; 00525 #endif 00526 } 00527 00528 00529 int KSSL::write(const void *buf, int len) { 00530 #ifdef KSSL_HAVE_SSL 00531 if (!m_bInit) 00532 return -1; 00533 00534 write_again: 00535 int rc = d->kossl->SSL_write(d->m_ssl, (const char *)buf, len); 00536 if (rc <= 0) { // OpenSSL returns 0 on error too 00537 int err = d->kossl->SSL_get_error(d->m_ssl, rc); 00538 00539 if (err == SSL_ERROR_WANT_WRITE) { 00540 ::usleep(20000); // 20ms sleep 00541 goto write_again; 00542 } 00543 00544 kdDebug(7029) << "SSL WRITE ERROR: " << err << endl; 00545 if (err != SSL_ERROR_NONE && 00546 err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL) 00547 rc = -1; 00548 } 00549 00550 return rc; 00551 #else 00552 return -1; 00553 #endif 00554 } 00555 00556 00557 bool KSSL::reconfig() { 00558 return reInitialize(); 00559 } 00560 00561 00562 void KSSL::setAutoReconfig(bool ar) { 00563 m_bAutoReconfig = ar; 00564 } 00565 00566 00567 bool KSSL::setSettings(KSSLSettings *settings) { 00568 delete m_cfg; 00569 m_cfg = settings; 00570 return reconfig(); 00571 } 00572 00573 00574 #ifdef KSSL_HAVE_SSL 00575 bool KSSL::m_bSSLWorks = true; 00576 #else 00577 bool KSSL::m_bSSLWorks = false; 00578 #endif 00579 00580 bool KSSL::doesSSLWork() { 00581 return m_bSSLWorks; 00582 } 00583 00584 00585 void KSSL::setConnectionInfo() { 00586 #ifdef KSSL_HAVE_SSL 00587 SSL_CIPHER *sc; 00588 char buf[1024]; 00589 00590 buf[0] = 0; // for safety. 00591 sc = d->kossl->SSL_get_current_cipher(d->m_ssl); 00592 if (!sc) { 00593 kdDebug(7029) << "KSSL get current cipher failed - we're probably gonna crash!" << endl; 00594 return; 00595 } 00596 00597 // set the number of bits, bits used 00598 m_ci.m_iCipherUsedBits = d->kossl->SSL_CIPHER_get_bits(sc, &(m_ci.m_iCipherBits)); 00599 // set the cipher version 00600 m_ci.m_cipherVersion = d->kossl->SSL_CIPHER_get_version(sc); 00601 // set the cipher name 00602 m_ci.m_cipherName = d->kossl->SSL_CIPHER_get_name(sc); 00603 // set the cipher description 00604 m_ci.m_cipherDescription = d->kossl->SSL_CIPHER_description(sc, buf, 1023); 00605 00606 #endif 00607 } 00608 00609 00610 void KSSL::setPeerInfo() { 00611 #ifdef KSSL_HAVE_SSL 00612 m_pi.setPeerHost(d->proxyPeer); 00613 m_pi.m_cert.setCert(d->kossl->SSL_get_peer_certificate(d->m_ssl)); 00614 STACK_OF(X509) *xs = d->kossl->SSL_get_peer_cert_chain(d->m_ssl); 00615 if (xs) 00616 xs = sk_X509_dup(xs); // Leak? 00617 m_pi.m_cert.setChain((void *)xs); 00618 #endif 00619 } 00620 00621 00622 KSSLConnectionInfo& KSSL::connectionInfo() { 00623 return m_ci; 00624 } 00625 00626 00627 // KDE 4: Make it const QString & 00628 void KSSL::setPeerHost(QString realHost) { 00629 d->proxyPeer = realHost; 00630 } 00631 00632 // deprecated 00633 void KSSL::setProxyUse(bool, QString, int, QString) { 00634 } 00635 00636 00637 KSSLPeerInfo& KSSL::peerInfo() { 00638 return m_pi; 00639 } 00640 00641 00642 bool KSSL::setClientCertificate(KSSLPKCS12 *pkcs) { 00643 #ifdef KSSL_HAVE_SSL 00644 if (!pkcs || !pkcs->getCertificate()) 00645 return false; 00646 00647 int rc; 00648 X509 *x = pkcs->getCertificate()->getCert(); 00649 EVP_PKEY *k = pkcs->getPrivateKey(); 00650 00651 if (!x || !k) return false; 00652 00653 if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient()) 00654 return false; 00655 00656 rc = d->kossl->SSL_CTX_use_certificate(d->m_ctx, x); 00657 if (rc <= 0) { 00658 kdDebug(7029) << "KSSL - SSL_CTX_use_certificate failed. rc = " << rc << endl; 00659 return false; 00660 } 00661 00662 rc = d->kossl->SSL_CTX_use_PrivateKey(d->m_ctx, k); 00663 if (rc <= 0) { 00664 kdDebug(7029) << "KSSL - SSL_CTX_use_PrivateKey failed. rc = " << rc << endl; 00665 return false; 00666 } 00667 00668 return true; 00669 #else 00670 return false; 00671 #endif 00672 } 00673 00674 #undef sk_dup 00675 00676 const KSSLSession* KSSL::session() const { 00677 return d->session; 00678 } 00679 00680 bool KSSL::reusingSession() const { 00681 #ifdef KSSL_HAVE_SSL 00682 return (d->m_ssl && d->kossl->SSL_session_reused(d->m_ssl)); 00683 #else 00684 return false; 00685 #endif 00686 } 00687 00688