/* * This file is part of the KFTPGrabber project * * Copyright (C) 2003-2006 by the KFTPGrabber developers * Copyright (C) 2003-2006 Jernej Kos * * 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 #include #include #include #include #include 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(qba.data()); #else unsigned char *qbap = reinterpret_cast(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() { #if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10000000) const SSL_CIPHER *cipher; #else SSL_CIPHER *cipher; #endif 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; } }