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