summaryrefslogtreecommitdiffstats
path: root/kftpgrabber/src/engine/ssl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kftpgrabber/src/engine/ssl.cpp')
-rw-r--r--kftpgrabber/src/engine/ssl.cpp264
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;
+}
+
+}