diff options
Diffstat (limited to 'kftpgrabber/src/engine/ssl.cpp')
-rw-r--r-- | kftpgrabber/src/engine/ssl.cpp | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/kftpgrabber/src/engine/ssl.cpp b/kftpgrabber/src/engine/ssl.cpp new file mode 100644 index 0000000..92418bb --- /dev/null +++ b/kftpgrabber/src/engine/ssl.cpp @@ -0,0 +1,264 @@ +/* + * This file is part of the KFTPGrabber project + * + * Copyright (C) 2003-2006 by the KFTPGrabber developers + * Copyright (C) 2003-2006 Jernej Kos <kostko@jweb-network.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +#include "ssl.h" + +#include <ksocketdevice.h> +#include <kmdcodec.h> +#include <ksslx509v3.h> + +#include <openssl/ssl.h> +#include <openssl/x509.h> + +#include <unistd.h> + +namespace KFTPEngine { + +class Ssl::Private { +public: + Private() + : ssl(0), sslCtx(0), bio(0) + { + } + + bool initialized; + + SSL *ssl; + SSL_CTX *sslCtx; + BIO *bio; + X509 *certificate; +}; + +Ssl::Ssl(KNetwork::KStreamSocket *socket) + : d(new Ssl::Private()), + m_socket(socket) +{ + d->ssl = 0; + d->sslCtx = 0; + d->bio = 0; + d->certificate = 0; + d->initialized = false; + + initialize(); +} + +Ssl::~Ssl() +{ + close(); + delete d; +} + +void Ssl::initialize() +{ + if (!d->ssl) { + SSL_library_init(); + + d->sslCtx = SSL_CTX_new(SSLv23_client_method()); + d->ssl = SSL_new(d->sslCtx); + + SSL_CTX_set_options(d->sslCtx, SSL_OP_ALL); + + // Initialize the socket BIO + d->bio = BIO_new_socket(m_socket->socketDevice()->socket(), BIO_NOCLOSE); + SSL_set_bio(d->ssl, d->bio, d->bio); + } + + d->initialized = true; +} + +bool Ssl::connect() +{ + if (!d->initialized) + return false; + +retry_connect: + int ret = SSL_connect(d->ssl); + if (ret == 1) { + // Connection established + setConnectionInfo(); + return true; + } else { + int err = SSL_get_error(d->ssl, ret); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { +retry_poll: + bool input; + m_socket->socketDevice()->poll(&input, 0, 0, 0); + + if (input) + goto retry_connect; + else { + ::usleep(20000); + goto retry_poll; + } + } else { + return false; + } + } + + return true; +} + +bool Ssl::setClientCertificate(KSSLPKCS12 *pkcs) +{ + if (!pkcs || !pkcs->getCertificate()) + return false; + + int ret; + X509 *x; + EVP_PKEY *k = pkcs->getPrivateKey(); + QCString cert = QCString(pkcs->getCertificate()->toString().ascii()); + + QByteArray qba, qbb = cert.copy(); + KCodecs::base64Decode(qbb, qba); +#if OPENSSL_VERSION_NUMBER > 0x009070afL + const unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data()); +#else + unsigned char *qbap = reinterpret_cast<unsigned char *>(qba.data()); +#endif + x = d2i_X509(NULL, &qbap, qba.size()); + + if (!x || !k) + return false; + + if (!pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient()) + return false; + + ret = SSL_CTX_use_certificate(d->sslCtx, x); + if (ret <= 0) + return false; + + ret = SSL_CTX_use_PrivateKey(d->sslCtx, k); + if (ret <= 0) + return false; + + return true; +} + +void Ssl::setConnectionInfo() +{ + SSL_CIPHER *cipher; + char buffer[1024]; + + buffer[0] = 0; + cipher = SSL_get_current_cipher(d->ssl); + + if (!cipher) + return; + + m_connectionInfo.m_cipherUsedBits = SSL_CIPHER_get_bits(cipher, &(m_connectionInfo.m_cipherBits)); + m_connectionInfo.m_cipherVersion = SSL_CIPHER_get_version(cipher); + m_connectionInfo.m_cipherName = SSL_CIPHER_get_name(cipher); + m_connectionInfo.m_cipherDescription = SSL_CIPHER_description(cipher, buffer, 1023); +} + +SslConnectionInfo &Ssl::connectionInfo() +{ + return m_connectionInfo; +} + +void Ssl::close() +{ + if (!d->initialized) + return; + + if (d->certificate) { + X509_free(d->certificate); + d->certificate = 0; + } + + if (d->ssl) { + SSL_shutdown(d->ssl); + SSL_free(d->ssl); + SSL_CTX_free(d->sslCtx); + + d->ssl = 0; + d->sslCtx = 0; + d->bio = 0; + } +} + +int Ssl::read(void *buffer, int bytes) +{ + if (!d->initialized) + return -1; + + int ret = SSL_read(d->ssl, buffer, bytes); + + if (ret <= 0) { + int err = SSL_get_error(d->ssl, ret); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) + return 0; + else + return -1; + } + + return ret; +} + +int Ssl::write(void *buffer, int bytes) +{ + if (!d->initialized) + return -1; + +retry_write: + int ret = SSL_write(d->ssl, buffer, bytes); + + if (ret <= 0) { + int err = SSL_get_error(d->ssl, ret); + + if (err == SSL_ERROR_WANT_READ) { +retry_poll: + bool input; + m_socket->socketDevice()->poll(&input, 0, 0, 0); + + if (input) + goto retry_write; + else { + ::usleep(20000); + goto retry_poll; + } + } else if (err == SSL_ERROR_WANT_WRITE) { + return -1; + } else { + return -1; + } + } + + return ret; +} + +} |