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     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 Home | KDE Accessibility Home | Description of Access Keys