• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kssl.cpp

Go to the documentation of this file.
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 #include "kssl.h"
00022 
00023 #include <config.h>
00024 #include <ksslconfig.h>
00025 
00026 // this hack provided by Malte Starostik to avoid glibc/openssl bug
00027 // on some systems
00028 #ifdef KSSL_HAVE_SSL
00029 #include <unistd.h>
00030 #include <netinet/in.h>
00031 #include <sys/socket.h>
00032 #define crypt _openssl_crypt
00033 #include <openssl/ssl.h>
00034 #include <openssl/x509.h>
00035 #include <openssl/x509v3.h>
00036 #include <openssl/pem.h>
00037 #include <openssl/rand.h>
00038 #undef crypt
00039 #endif
00040 
00041 #include <kdebug.h>
00042 #include <kstandarddirs.h>
00043 
00044 #include <kopenssl.h>
00045 #include <ksslx509v3.h>
00046 #include <ksslpkcs12.h>
00047 #include <ksslsession.h>
00048 #include <klocale.h>
00049 
00050 #include <QtNetwork/QAbstractSocket>
00051 #include <k3clientsocketbase.h>
00052 #include <k3socketdevice.h>
00053 
00054 #ifdef __GNUC__
00055 #warning "kssl.cc contains temporary functions! Clean up"
00056 #warning "kssl.cc needs to be ported to QSslSocket"
00057 #endif
00058 
00059 #define sk_dup d->kossl->sk_dup
00060 
00061 class KSSLPrivate {
00062 public:
00063     KSSLPrivate() {
00064         lastInitTLS = false;
00065         kossl = KOpenSSLProxy::self();
00066         session = 0L;
00067     }
00068 
00069     ~KSSLPrivate() {
00070         delete session;
00071         session = 0L;
00072     }
00073 
00074     bool lastInitTLS;
00075     KSSLCertificate::KSSLValidation m_cert_vfy_res;
00076     QString proxyPeer;
00077 
00078 #ifdef KSSL_HAVE_SSL
00079     SSL *m_ssl;
00080     SSL_CTX *m_ctx;
00081     SSL_METHOD *m_meth;
00082 #endif
00083     KSSLSession *session;
00084     KOSSL *kossl;
00085 };
00086 
00087 
00088 KSSL::KSSL(bool init) {
00089     d = new KSSLPrivate;
00090     m_bInit = false;
00091     m_bAutoReconfig = true;
00092     m_cfg = new KSSLSettings();
00093 #ifdef KSSL_HAVE_SSL
00094     d->m_ssl = 0L;
00095 #endif
00096 
00097     if (init)
00098         initialize();
00099 }
00100 
00101 
00102 KSSL::~KSSL() {
00103     close();
00104     delete m_cfg;
00105     delete d;
00106 }
00107 
00108 
00109 int KSSL::seedWithEGD() {
00110 int rc = 0;
00111 #ifdef KSSL_HAVE_SSL
00112     if (m_cfg->useEGD() && !m_cfg->getEGDPath().isEmpty()) {
00113         rc = d->kossl->RAND_egd(m_cfg->getEGDPath().toLatin1().constData());
00114         if (rc < 0)
00115             kDebug(7029) << "KSSL: Error seeding PRNG with the EGD.";
00116         else kDebug(7029) << "KSSL: PRNG was seeded with " << rc
00117                    << " bytes from the EGD." << endl;
00118     } else if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) {
00119         rc = d->kossl->RAND_load_file(m_cfg->getEGDPath().toLatin1().constData(), -1);
00120         if (rc < 0)
00121             kDebug(7029) << "KSSL: Error seeding PRNG with the entropy file.";
00122         else kDebug(7029) << "KSSL: PRNG was seeded with " << rc
00123                    << " bytes from the entropy file." << endl;
00124     }
00125 #endif
00126 return rc;
00127 }
00128 
00129 
00130 bool KSSL::TLSInit() {
00131 #ifdef KSSL_HAVE_SSL
00132 // kDebug(7029) << "KSSL TLS initialize";
00133     if (m_bInit)
00134         return false;
00135 
00136     if (m_bAutoReconfig)
00137         m_cfg->load();
00138 
00139     seedWithEGD();
00140     d->m_meth = d->kossl->TLSv1_client_method();
00141     d->lastInitTLS = true;
00142 
00143     m_pi.reset();
00144 
00145     d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth);
00146     if (d->m_ctx == 0L) {
00147         return false;
00148     }
00149 
00150     // set cipher list
00151     QString clist = m_cfg->getCipherList();
00152     //kDebug(7029) << "Cipher list: " << clist;
00153     if (!clist.isEmpty())
00154         d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.toAscii().constData()));
00155 
00156     m_bInit = true;
00157 return true;
00158 #else
00159 return false;
00160 #endif
00161 }
00162 
00163 
00164 bool KSSL::initialize() {
00165 #ifdef KSSL_HAVE_SSL
00166     kDebug(7029) << "KSSL initialize";
00167     if (m_bInit)
00168         return false;
00169 
00170     if (m_bAutoReconfig)
00171         m_cfg->load();
00172 
00173     seedWithEGD();
00174     d->lastInitTLS = false;
00175 
00176     m_pi.reset();
00177 
00178     d->m_meth = d->kossl->SSLv23_client_method();
00179     d->m_ctx = d->kossl->SSL_CTX_new(d->m_meth);
00180     if (d->m_ctx == 0L) {
00181         return false;
00182     }
00183 
00184     // set cipher list
00185     QString clist = m_cfg->getCipherList();
00186     kDebug(7029) << "Cipher list: " << clist;
00187     if (!clist.isEmpty())
00188         d->kossl->SSL_CTX_set_cipher_list(d->m_ctx, const_cast<char *>(clist.toAscii().constData()));
00189 
00190     m_bInit = true;
00191 return true;
00192 #else
00193 return false;
00194 #endif
00195 }
00196 
00197 
00198 bool KSSL::setSession(const KSSLSession *session) {
00199 #ifdef KSSL_HAVE_SSL
00200     if (!session) {
00201         delete d->session;
00202         d->session = 0L;
00203         return true;
00204     }
00205 
00206     // Obtain a reference by incrementing the reference count.  Yuck.
00207     static_cast<SSL_SESSION*>(session->_session)->references++;
00208 
00209     d->session = new KSSLSession;
00210     d->session->_session = session->_session;
00211 
00212     return true;
00213 #else
00214     return false;
00215 #endif
00216 }
00217 
00218 
00219 void KSSL::close() {
00220 #ifdef KSSL_HAVE_SSL
00221 //kDebug(7029) << "KSSL close";
00222     if (!m_bInit)
00223         return;
00224 
00225     delete d->session;
00226     d->session = 0L;
00227 
00228     if (d->m_ssl) {
00229         d->kossl->SSL_shutdown(d->m_ssl);
00230         d->kossl->SSL_free(d->m_ssl);
00231         d->m_ssl = 0L;
00232     }
00233 
00234     d->kossl->SSL_CTX_free(d->m_ctx);
00235     if (m_cfg->useEFile() && !m_cfg->getEGDPath().isEmpty()) {
00236         d->kossl->RAND_write_file(m_cfg->getEGDPath().toLatin1().constData());
00237     }
00238 
00239     m_bInit = false;
00240 #endif
00241 }
00242 
00243 
00244 bool KSSL::reInitialize() {
00245     close();
00246 return initialize();
00247 }
00248 
00249 // get the callback file - it's hidden away in here
00250 //#include "ksslcallback.c"
00251 
00252 
00253 // KDE4 FIXME: temporary code
00254 // I have no idea if this even works!
00255 int KSSL::accept(QIODevice* dev) {
00256     QAbstractSocket* socket = qobject_cast<QAbstractSocket*>(dev);
00257     if (socket)
00258             return accept(socket->socketDescriptor()); // doubtful if this works...
00259 
00260         KNetwork::KClientSocketBase *othersocket = qobject_cast<KNetwork::KClientSocketBase*>(dev);
00261         if (othersocket)
00262             return accept(othersocket->socketDevice()->socket());
00263 
00264         return -1;
00265 }
00266 
00267 int KSSL::accept(int sock) {
00268 #ifdef KSSL_HAVE_SSL
00269 // kDebug(7029) << "KSSL accept";
00270 int rc;
00271     if (!m_bInit)
00272         return -1;
00273     d->m_ssl = d->kossl->SSL_new(d->m_ctx);
00274     if (!d->m_ssl)
00275         return -1;
00276 
00277     if (d->session) {
00278         if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0)
00279         {
00280             kDebug(7029) << "Can't reuse session, no certificate.";
00281             delete d->session;
00282             d->session = 0;
00283         } else if (1 == d->kossl->SSL_set_session(d->m_ssl,
00284             static_cast<SSL_SESSION*>(d->session->_session))) {
00285             kDebug(7029) << "Session ID is being reused.";
00286         } else {
00287             kDebug(7029) << "Error attempting to reuse session.";
00288             delete d->session;
00289             d->session = 0;
00290         }
00291     }
00292 
00293     d->kossl->SSL_set_options(d->m_ssl, SSL_OP_ALL|SSL_OP_NO_SSLv2);
00294 
00295     rc = d->kossl->SSL_set_fd(d->m_ssl, sock);
00296     if (rc == 0) {
00297         d->kossl->SSL_shutdown(d->m_ssl);
00298         d->kossl->SSL_free(d->m_ssl);
00299         d->m_ssl = 0;
00300         return rc;
00301     }
00302 
00303     rc = d->kossl->SSL_accept(d->m_ssl);
00304     if (rc == 1) {
00305         setConnectionInfo();
00306         setPeerInfo();
00307         kDebug(7029) << "KSSL connected OK";
00308     } else {
00309         kDebug(7029) << "KSSL accept failed - rc = " << rc;
00310         kDebug(7029) << "                      ERROR = "
00311                   << d->kossl->SSL_get_error(d->m_ssl, rc) << endl;
00312         d->kossl->SSL_shutdown(d->m_ssl);
00313         d->kossl->SSL_free(d->m_ssl);
00314         d->m_ssl = 0;
00315         return -1;
00316     }
00317 
00318     if (!d->kossl->SSL_session_reused(d->m_ssl)) {
00319         if (d->session) {
00320             kDebug(7029) << "Session reuse failed.  New session used instead.";
00321             delete d->session;
00322             d->session = 0L;
00323         }
00324     }
00325 
00326     if (!d->session) {
00327         SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl);
00328         if (sess) {
00329             d->session = new KSSLSession;
00330             d->session->_session = sess;
00331         }
00332     }
00333 
00334 return rc;
00335 #else
00336 return -1;
00337 #endif
00338 }
00339 
00340 // KDE4 FIXME: temporary code
00341 int KSSL::connect(QIODevice* dev) {
00342     QAbstractSocket* socket = qobject_cast<QAbstractSocket*>(dev);
00343     if (socket)
00344             return connect(socket->socketDescriptor()); // doubtful if this works...
00345 
00346         KNetwork::KClientSocketBase *othersocket = qobject_cast<KNetwork::KClientSocketBase*>(dev);
00347         if (othersocket)
00348             return connect(othersocket->socketDevice()->socket());
00349 
00350         return -1;
00351 }
00352 
00353 
00354 int KSSL::connect(int sock) {
00355 #ifdef KSSL_HAVE_SSL
00356 // kDebug(7029) << "KSSL connect";
00357 int rc;
00358     if (!m_bInit)
00359         return -1;
00360     d->m_ssl = d->kossl->SSL_new(d->m_ctx);
00361     if (!d->m_ssl)
00362         return -1;
00363 
00364     if (d->session) {
00365         if (static_cast<SSL_SESSION*>(d->session->_session)->sess_cert == 0)
00366         {
00367             kDebug(7029) << "Can't reuse session, no certificate.";
00368             delete d->session;
00369             d->session = 0;
00370         } else if (1 == d->kossl->SSL_set_session(d->m_ssl,
00371             static_cast<SSL_SESSION*>(d->session->_session))) {
00372             kDebug(7029) << "Session ID is being reused.";
00373         } else {
00374             kDebug(7029) << "Error attempting to reuse session.";
00375             delete d->session;
00376             d->session = 0;
00377         }
00378     }
00379 
00380     d->kossl->SSL_set_options(d->m_ssl, SSL_OP_ALL|SSL_OP_NO_SSLv2);
00381 
00382     rc = d->kossl->SSL_set_fd(d->m_ssl, sock);
00383     if (rc == 0) {
00384         d->kossl->SSL_shutdown(d->m_ssl);
00385         d->kossl->SSL_free(d->m_ssl);
00386         d->m_ssl = 0;
00387         return rc;
00388     }
00389 
00390 connect_again:
00391     rc = d->kossl->SSL_connect(d->m_ssl);
00392     if (rc == 1) {
00393         setConnectionInfo();
00394         setPeerInfo();
00395         kDebug(7029) << "KSSL connected OK";
00396     } else {
00397         int err = d->kossl->SSL_get_error(d->m_ssl, rc);
00398         if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
00399             // nonblocking - but we block anyways in connect() :)
00400             goto connect_again;
00401         } else {
00402             kDebug(7029) << "KSSL connect failed - rc = "
00403                 << rc << endl;
00404             kDebug(7029) << "                   ERROR = "
00405                 << err << endl;
00406             d->kossl->ERR_print_errors_fp(stderr);
00407             d->kossl->SSL_shutdown(d->m_ssl);
00408             d->kossl->SSL_free(d->m_ssl);
00409             d->m_ssl = 0;
00410             return -1;
00411         }
00412     }
00413 
00414     if (!d->kossl->SSL_session_reused(d->m_ssl)) {
00415         if (d->session) {
00416             kDebug(7029) << "Session reuse failed.  New session used instead.";
00417             delete d->session;
00418             d->session = 0L;
00419         }
00420     }
00421 
00422     if (!d->session) {
00423         SSL_SESSION *sess = d->kossl->SSL_get1_session(d->m_ssl);
00424         if (sess) {
00425             d->session = new KSSLSession;
00426             d->session->_session = sess;
00427         }
00428     }
00429 
00430 return rc;
00431 #else
00432 return -1;
00433 #endif
00434 }
00435 
00436 
00437 int KSSL::pending() {
00438 #ifdef KSSL_HAVE_SSL
00439     if (!m_bInit)
00440         return -1;
00441 return d->kossl->SSL_pending(d->m_ssl);
00442 #else
00443 return -1;
00444 #endif
00445 }
00446 
00447 
00448 int KSSL::peek(char *buf, int len) {
00449 #ifdef KSSL_HAVE_SSL
00450     if (!m_bInit)
00451         return -1;
00452     // FIXME: enhance to work the way read() does below, handling errors
00453 return d->kossl->SSL_peek(d->m_ssl, buf, len);
00454 #else
00455 return -1;
00456 #endif
00457 }
00458 
00459 
00460 int KSSL::read(char *buf, int len) {
00461 #ifdef KSSL_HAVE_SSL
00462     int rc = 0;
00463     int maxIters = 10;
00464 
00465     if (!m_bInit)
00466         return -1;
00467 
00468 read_again:
00469     rc = d->kossl->SSL_read(d->m_ssl, (char *)buf, len);
00470     if (rc <= 0) {
00471         int err = d->kossl->SSL_get_error(d->m_ssl, rc);
00472 
00473         if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
00474             kDebug(7029) << "SSL read() returning 0: " << err;
00475             if (maxIters-- > 0) {
00476                 ::usleep(20000); // 20ms sleep
00477                 goto read_again;
00478             }
00479             return 0;
00480         }
00481 
00482         kDebug(7029) << "SSL READ ERROR: " << err;
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 char *buf, int len) {
00499 #ifdef KSSL_HAVE_SSL
00500     if (!m_bInit)
00501         return -1;
00502 
00503 write_again:
00504     int rc = d->kossl->SSL_write(d->m_ssl, (const char *)buf, len);
00505     if (rc <= 0) {      // OpenSSL returns 0 on error too
00506         int err = d->kossl->SSL_get_error(d->m_ssl, rc);
00507 
00508         if (err == SSL_ERROR_WANT_WRITE) {
00509             ::usleep(20000); // 20ms sleep
00510             goto write_again;
00511         }
00512 
00513         kDebug(7029) << "SSL WRITE ERROR: " << err;
00514         if (err != SSL_ERROR_NONE &&
00515             err != SSL_ERROR_ZERO_RETURN && err != SSL_ERROR_SYSCALL)
00516             rc = -1;
00517     }
00518 
00519 return rc;
00520 #else
00521 return -1;
00522 #endif
00523 }
00524 
00525 
00526 bool KSSL::reconfig() {
00527     return reInitialize();
00528 }
00529 
00530 
00531 void KSSL::setAutoReconfig(bool ar) {
00532     m_bAutoReconfig = ar;
00533 }
00534 
00535 
00536 bool KSSL::setSettings(KSSLSettings *settings) {
00537     delete m_cfg;
00538     m_cfg = settings;
00539     return reconfig();
00540 }
00541 
00542 KSSLSettings * KSSL::settings()
00543 {
00544     return m_cfg;
00545 }
00546 
00547 
00548 #ifdef KSSL_HAVE_SSL
00549 bool KSSL::m_bSSLWorks = true;
00550 #else
00551 bool KSSL::m_bSSLWorks = false;
00552 #endif
00553 
00554 bool KSSL::doesSSLWork() {
00555     return m_bSSLWorks;
00556 }
00557 
00558 
00559 void KSSL::setConnectionInfo() {
00560 #ifdef KSSL_HAVE_SSL
00561 SSL_CIPHER *sc;
00562 char buf[1024];
00563 
00564     buf[0] = 0;  // for safety.
00565     sc = d->kossl->SSL_get_current_cipher(d->m_ssl);
00566     if (!sc) {
00567         kDebug(7029) << "KSSL get current cipher failed - we're probably gonna crash!";
00568         return;
00569     }
00570 
00571     // set the number of bits, bits used
00572     m_ci.m_iCipherUsedBits = d->kossl->SSL_CIPHER_get_bits(sc, &(m_ci.m_iCipherBits));
00573     // set the cipher version
00574     m_ci.m_cipherVersion = d->kossl->SSL_CIPHER_get_version(sc);
00575     // set the cipher name
00576     m_ci.m_cipherName = d->kossl->SSL_CIPHER_get_name(sc);
00577     // set the cipher description
00578     m_ci.m_cipherDescription = d->kossl->SSL_CIPHER_description(sc, buf, 1023);
00579 
00580 #endif
00581 }
00582 
00583 
00584 void KSSL::setPeerInfo() {
00585 #ifdef KSSL_HAVE_SSL
00586     m_pi.setPeerHost(d->proxyPeer);
00587     m_pi.m_cert.setCert(d->kossl->SSL_get_peer_certificate(d->m_ssl));
00588     STACK_OF(X509) *xs = d->kossl->SSL_get_peer_cert_chain(d->m_ssl);
00589     if (xs)
00590         xs = sk_X509_dup(xs);   // Leak?
00591     m_pi.m_cert.setChain((void *)xs);
00592 #endif
00593 }
00594 
00595 
00596 KSSLConnectionInfo& KSSL::connectionInfo() {
00597     return m_ci;
00598 }
00599 
00600 
00601 void KSSL::setPeerHost(const QString& realHost) {
00602     d->proxyPeer = realHost;
00603 }
00604 
00605 KSSLPeerInfo& KSSL::peerInfo() {
00606     return m_pi;
00607 }
00608 
00609 
00610 bool KSSL::setClientCertificate(KSSLPKCS12 *pkcs) {
00611 #ifdef KSSL_HAVE_SSL
00612     if (!pkcs || !pkcs->getCertificate())
00613         return false;
00614 
00615 int rc;
00616 X509 *x = pkcs->getCertificate()->getCert();
00617 EVP_PKEY *k = pkcs->getPrivateKey();
00618 
00619     if (!x || !k) return false;
00620 
00621     if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())
00622         return false;
00623 
00624     rc = d->kossl->SSL_CTX_use_certificate(d->m_ctx, x);
00625     if (rc <= 0) {
00626         kDebug(7029) << "KSSL - SSL_CTX_use_certificate failed.  rc = " << rc;
00627         return false;
00628     }
00629 
00630     rc = d->kossl->SSL_CTX_use_PrivateKey(d->m_ctx, k);
00631     if (rc <= 0) {
00632         kDebug(7029) << "KSSL - SSL_CTX_use_PrivateKey failed.  rc = " << rc;
00633         return false;
00634     }
00635 
00636     return true;
00637 #else
00638     return false;
00639 #endif
00640 }
00641 
00642 #undef sk_dup
00643 
00644 const KSSLSession* KSSL::session() const {
00645     return d->session;
00646 }
00647 
00648 bool KSSL::reusingSession() const {
00649 #ifdef KSSL_HAVE_SSL
00650     return (d->m_ssl && d->kossl->SSL_session_reused(d->m_ssl));
00651 #else
00652     return false;
00653 #endif
00654 }
00655 
00656 

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal