Add OpenSSL support to client

This commit is contained in:
Martin 2019-08-03 18:55:00 +02:00
parent 28bc800198
commit 8a4843a349
8 changed files with 498 additions and 4 deletions

View File

@ -5,7 +5,7 @@ bin_PROGRAMS = urbackupclientctl urbackupclientgui
else
bin_PROGRAMS = urbackupclientctl
endif
urbackupclientbackend_SOURCES = AcceptThread.cpp Client.cpp Database.cpp Query.cpp SelectThread.cpp Server.cpp ServerLinux.cpp ServiceAcceptor.cpp ServiceWorker.cpp SessionMgr.cpp StreamPipe.cpp Template.cpp WorkerThread.cpp main.cpp md5.cpp stringtools.cpp libfastcgi/fastcgi.cpp Mutex_lin.cpp LoadbalancerClient.cpp DBSettingsReader.cpp file_common.cpp file_fstream.cpp file_linux.cpp FileSettingsReader.cpp LookupService.cpp SettingsReader.cpp Table.cpp OutputStream.cpp ThreadPool.cpp MemoryPipe.cpp Condition_lin.cpp MemorySettingsReader.cpp sqlite/shell.c SQLiteFactory.cpp PipeThrottler.cpp mt19937ar.cpp DatabaseCursor.cpp SharedMutex_lin.cpp StaticPluginRegistration.cpp common/data.cpp common/adler32.cpp
urbackupclientbackend_SOURCES = AcceptThread.cpp Client.cpp Database.cpp Query.cpp SelectThread.cpp Server.cpp ServerLinux.cpp ServiceAcceptor.cpp ServiceWorker.cpp SessionMgr.cpp StreamPipe.cpp Template.cpp WorkerThread.cpp main.cpp md5.cpp stringtools.cpp libfastcgi/fastcgi.cpp Mutex_lin.cpp LoadbalancerClient.cpp DBSettingsReader.cpp file_common.cpp file_fstream.cpp file_linux.cpp FileSettingsReader.cpp LookupService.cpp SettingsReader.cpp Table.cpp OutputStream.cpp ThreadPool.cpp MemoryPipe.cpp Condition_lin.cpp MemorySettingsReader.cpp sqlite/shell.c SQLiteFactory.cpp PipeThrottler.cpp mt19937ar.cpp DatabaseCursor.cpp SharedMutex_lin.cpp StaticPluginRegistration.cpp common/data.cpp common/adler32.cpp OpenSSLPipe.cpp
if WITH_EMBEDDED_SQLITE3
urbackupclientbackend_SOURCES += sqlite/sqlite3.c
@ -71,6 +71,12 @@ urbackupclientbackend_CPPFLAGS += $(SQLITE3_CFLAGS) -DUSE_SYSTEM_SQLITE
urbackupclientbackend_LDFLAGS += $(SQLITE3_LDFLAGS)
endif
if WITH_OPENSSL
urbackupclientbackend_CPPFLAGS += $(OPENSSL_INCLUDES) -DWITH_OPENSSL
urbackupclientbackend_LDADD += $(OPENSSL_LIBS)
urbackupclientbackend_LDFLAGS += $(OPENSSL_LDFLAGS)
endif
urbackupclientctl_SOURCES = clientctl/main.cpp urbackupcommon/os_functions_lin.cpp stringtools.cpp clientctl/Connector.cpp clientctl/tcpstack.cpp urbackupcommon/escape.cpp clientctl/jsoncpp.cpp
urbackupclientctl_LDADD =
urbackupclientctl_CXXFLAGS = -DOS_FUNC_NO_SERVER -DLINUX -DVARDIR='"$(localstatedir)"'
@ -250,7 +256,7 @@ tclap_headers = \
tclap/ArgTraits.h \
tclap/StandardTraits.h
noinst_HEADERS=SessionMgr.h WorkerThread.h Helper_win32.h Database.h defaults.h ServiceAcceptor.h Query.h SettingsReader.h file.h file_memory.h MemorySettingsReader.h Condition_lin.h LookupService.h Template.h types.h DBSettingsReader.h stringtools.h ThreadPool.h libs.h vld_.h ServiceWorker.h StreamPipe.h LoadbalancerClient.h socket_header.h FileSettingsReader.h SelectThread.h md5.h vld.h Table.h Client.h MemoryPipe.h Mutex_lin.h AcceptThread.h OutputStream.h Server.h Interface/SessionMgr.h Interface/Service.h Interface/PluginMgr.h Interface/Database.h Interface/Pipe.h Interface/CustomClient.h Interface/User.h Interface/Query.h Interface/SettingsReader.h Interface/Types.h Interface/Template.h Interface/ThreadPool.h Interface/Mutex.h Interface/File.h Interface/Condition.h Interface/Table.h Interface/Plugin.h Interface/Thread.h Interface/Action.h Interface/Object.h Interface/OutputStream.h Interface/Server.h libfastcgi/fastcgi.hpp sqlite/sqlite3.h sqlite/sqlite3ext.h utf8/utf8.h utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h cryptoplugin/ICryptoFactory.h cryptoplugin/IAESEncryption.h cryptoplugin/IAESDecryption.h Interface/DatabaseFactory.h Interface/DatabaseInt.h sqlite/shell.h SQLiteFactory.h PipeThrottler.h Interface/PipeThrottler.h mt19937ar.h DatabaseCursor.h Interface/DatabaseCursor.h client_version.h Interface/SharedMutex.h SharedMutex_lin.h StaticPluginRegistration.h common/bitmap.h $(cryptoplugin_headers) $(fileservplugin_headers) $(fsimageplugin_headers) $(urbackupclientctl_headers) $(client_headers) $(tclap_headers) $(urbackupclient_headers) $(cryptopp_headers)
noinst_HEADERS=SessionMgr.h WorkerThread.h Helper_win32.h Database.h defaults.h ServiceAcceptor.h Query.h SettingsReader.h file.h file_memory.h MemorySettingsReader.h Condition_lin.h LookupService.h Template.h types.h DBSettingsReader.h stringtools.h ThreadPool.h libs.h vld_.h ServiceWorker.h StreamPipe.h LoadbalancerClient.h socket_header.h FileSettingsReader.h SelectThread.h md5.h vld.h Table.h Client.h MemoryPipe.h Mutex_lin.h AcceptThread.h OutputStream.h Server.h Interface/SessionMgr.h Interface/Service.h Interface/PluginMgr.h Interface/Database.h Interface/Pipe.h Interface/CustomClient.h Interface/User.h Interface/Query.h Interface/SettingsReader.h Interface/Types.h Interface/Template.h Interface/ThreadPool.h Interface/Mutex.h Interface/File.h Interface/Condition.h Interface/Table.h Interface/Plugin.h Interface/Thread.h Interface/Action.h Interface/Object.h Interface/OutputStream.h Interface/Server.h libfastcgi/fastcgi.hpp sqlite/sqlite3.h sqlite/sqlite3ext.h utf8/utf8.h utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h cryptoplugin/ICryptoFactory.h cryptoplugin/IAESEncryption.h cryptoplugin/IAESDecryption.h Interface/DatabaseFactory.h Interface/DatabaseInt.h sqlite/shell.h SQLiteFactory.h PipeThrottler.h Interface/PipeThrottler.h mt19937ar.h DatabaseCursor.h Interface/DatabaseCursor.h client_version.h Interface/SharedMutex.h SharedMutex_lin.h StaticPluginRegistration.h common/bitmap.h OpenSSLPipe.h $(cryptoplugin_headers) $(fileservplugin_headers) $(fsimageplugin_headers) $(urbackupclientctl_headers) $(client_headers) $(tclap_headers) $(urbackupclient_headers) $(cryptopp_headers)
EXTRA_DIST_GUI = client/info.txt client/data/backup-bad.xpm client/data/backup-ok.xpm client/data/backup-progress.xpm client/data/backup-progress-pause.xpm client/data/backup-no-server.xpm client/data/backup-no-recent.xpm client/data/backup-indexing.xpm client/data/logo1.png client/data/lang/it/urbackup.mo client/data/lang/pl/urbackup.mo client/data/lang/pt_BR/urbackup.mo client/data/lang/sk/urbackup.mo client/data/lang/zh_TW/urbackup.mo client/data/lang/zh_CN/urbackup.mo client/data/lang/de/urbackup.mo client/data/lang/es/urbackup.mo client/data/lang/fr/urbackup.mo client/data/lang/ru/urbackup.mo client/data/lang/uk/urbackup.mo client/data/lang/da/urbackup.mo client/data/lang/nl/urbackup.mo client/data/lang/fa/urbackup.mo client/data/lang/cs/urbackup.mo client/gui/GUISetupWizard.h client/SetupWizard.h

344
OpenSSLPipe.cpp Normal file
View File

@ -0,0 +1,344 @@
#ifdef WITH_OPENSSL
#include "OpenSSLPipe.h"
#include <openssl/ssl.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#include "Server.h"
OpenSSLPipe::OpenSSLPipe(CStreamPipe * bpipe)
: bpipe(bpipe), bbio(NULL), ctx(NULL),
has_error(false)
{
}
OpenSSLPipe::~OpenSSLPipe()
{
if (bbio != NULL)
BIO_free_all(bbio);
if (ctx != NULL)
SSL_CTX_free(ctx);
}
void OpenSSLPipe::init()
{
SSL_library_init();
SSL_load_error_strings();
OPENSSL_config(NULL);
}
namespace
{
void log_ssl_err()
{
unsigned long err = ERR_get_error();
if (err != 0)
{
const char* const str = ERR_reason_error_string(err);
if (str)
{
Server->Log(std::string("OpenSSL error: ") + str, LL_WARNING);
}
}
}
const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS";
}
bool OpenSSLPipe::ssl_connect(const std::string & p_hostname, int timeoutms)
{
const SSL_METHOD* method = SSLv23_method();
if (method == NULL)
{
log_ssl_err();
return false;
}
ctx = SSL_CTX_new(method);
if (ctx == NULL)
{
log_ssl_err();
return false;
}
SSL_CTX_set_verify_depth(ctx, 5);
const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
SSL_CTX_set_options(ctx, flags);
long res = SSL_CTX_set_default_verify_paths(ctx);
if (res != 1)
{
log_ssl_err();
return false;
}
/*long res = SSL_CTX_load_verify_locations(ctx, "D:\\tmp\\cacert-2019-05-15.pem", NULL);
if (res != 1)
{
log_ssl_err();
return false;
}*/
bbio = BIO_new_ssl(ctx, 1);
if (bbio == NULL)
{
log_ssl_err();
return false;
}
BIO* sbio = BIO_new_socket(static_cast<int>(bpipe->getSocket()), BIO_NOCLOSE);
if (sbio == NULL)
{
log_ssl_err();
return false;
}
BIO_push(bbio, sbio);
BIO_set_nbio(bbio, 1);
SSL *ssl = NULL;
BIO_get_ssl(bbio, &ssl);
if (ssl == NULL)
{
log_ssl_err();
return false;
}
res = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS);
if (res!=1)
{
log_ssl_err();
return false;
}
res = SSL_set_tlsext_host_name(ssl, p_hostname.c_str());
if (res != 1)
{
log_ssl_err();
return false;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
X509_VERIFY_PARAM* param = SSL_get0_param(ssl);
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
res = X509_VERIFY_PARAM_set1_host(param, p_hostname.c_str(), p_hostname.size());
#else
SSL_set_hostflags(ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
res = SSL_set1_host(ssl, p_hostname.c_str());
#endif
if (res != 1)
{
log_ssl_err();
return false;
}
SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
int64 starttime = Server->getTimeMS();
do
{
res = BIO_do_handshake(bbio);
if (res != 1
&& !BIO_should_retry(bbio) )
{
log_ssl_err();
return false;
}
if (res == 1)
break;
int64 rtime = timeoutms - (Server->getTimeMS() - starttime);
if (rtime < 0)
rtime = 0;
if (timeoutms < 0)
rtime = -1;
if (!bpipe->isReadOrWritable(static_cast<int>(rtime)))
break;
} while (Server->getTimeMS() - starttime < timeoutms);
if (res != 1)
{
Server->Log("SSL connect timeout", LL_WARNING);
return false;
}
X509* cert = SSL_get_peer_certificate(ssl);
if (cert)
{
X509_free(cert);
}
if (cert == NULL)
{
Server->Log("Getting server certificate failed", LL_WARNING);
return false;
}
res = SSL_get_verify_result(ssl);
if (res != X509_V_OK)
{
const char* const str = ERR_reason_error_string(res);
if (str)
{
Server->Log("Verifying certificate of hostname " + p_hostname + " failed with OpenSSL error code " + str, LL_WARNING);
}
return false;
}
return true;
}
size_t OpenSSLPipe::Read(char * buffer, size_t bsize, int timeoutms)
{
if (BIO_pending(bbio)<=0)
{
if (!bpipe->isReadable(timeoutms))
{
return 0;
}
}
int rc = BIO_read(bbio, buffer, static_cast<int>(bsize));
if (rc <= 0)
{
if (!BIO_should_retry(bbio))
{
has_error = true;
}
return 0;
}
else
{
bpipe->doThrottle(rc, false, true);
return rc;
}
}
bool OpenSSLPipe::Write(const char * buffer, size_t bsize, int timeoutms, bool flush)
{
if (bsize == 0)
return true;
if (!bpipe->isWritable(timeoutms))
{
return false;
}
int rc = BIO_write(bbio, buffer, static_cast<int>(bsize));
if (rc <= 0)
{
if (!BIO_should_retry(bbio))
{
has_error = true;
}
return false;
}
else
{
if (rc < bsize)
{
bpipe->doThrottle(rc, true, true);
return Write(buffer + rc, bsize - rc, -1, flush);
}
return true;
}
}
size_t OpenSSLPipe::Read(std::string * ret, int timeoutms)
{
char buffer[8192];
size_t l = Read(buffer, 8192, timeoutms);
if (l>0)
{
ret->assign(buffer, l);
}
else
{
return 0;
}
return l;
}
bool OpenSSLPipe::Write(const std::string & str, int timeoutms, bool flush)
{
return Write(&str[0], str.size(), timeoutms, flush);
}
bool OpenSSLPipe::Flush(int timeoutms)
{
return bpipe->Flush(timeoutms);
}
bool OpenSSLPipe::isWritable(int timeoutms)
{
return bpipe->isWritable(timeoutms);
}
bool OpenSSLPipe::isReadable(int timeoutms)
{
return BIO_pending(bbio)>0 || bpipe->isReadable(timeoutms);
}
bool OpenSSLPipe::hasError(void)
{
return has_error || bpipe->hasError();
}
void OpenSSLPipe::shutdown(void)
{
bpipe->shutdown();
}
size_t OpenSSLPipe::getNumElements(void)
{
return bpipe->getNumElements();
}
void OpenSSLPipe::addThrottler(IPipeThrottler * throttler)
{
bpipe->addThrottler(throttler);
}
void OpenSSLPipe::addOutgoingThrottler(IPipeThrottler * throttler)
{
bpipe->addOutgoingThrottler(throttler);
}
void OpenSSLPipe::addIncomingThrottler(IPipeThrottler * throttler)
{
bpipe->addIncomingThrottler(throttler);
}
_i64 OpenSSLPipe::getTransferedBytes(void)
{
return bpipe->getTransferedBytes();
}
void OpenSSLPipe::resetTransferedBytes(void)
{
bpipe->resetTransferedBytes();
}
#endif //WITH_OPENSSL

61
OpenSSLPipe.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#ifdef WITH_OPENSSL
#include "Interface/Pipe.h"
#include "StreamPipe.h"
#include <memory>
#include <openssl/bio.h>
class OpenSSLPipe : public IPipe
{
public:
OpenSSLPipe(CStreamPipe* bpipe);
~OpenSSLPipe();
static void init();
bool ssl_connect(const std::string& p_hostname, int timeoutms);
// Inherited via IPipe
virtual size_t Read(char * buffer, size_t bsize, int timeoutms = -1);
virtual bool Write(const char * buffer, size_t bsize, int timeoutms = -1, bool flush = true);
virtual size_t Read(std::string * ret, int timeoutms = -1);
virtual bool Write(const std::string & str, int timeoutms = -1, bool flush = true);
virtual bool Flush(int timeoutms = -1);
virtual bool isWritable(int timeoutms = 0);
virtual bool isReadable(int timeoutms = 0);
virtual bool hasError(void);
virtual void shutdown(void);
virtual size_t getNumElements(void);
virtual void addThrottler(IPipeThrottler * throttler);
virtual void addOutgoingThrottler(IPipeThrottler * throttler);
virtual void addIncomingThrottler(IPipeThrottler * throttler);
virtual _i64 getTransferedBytes(void);
virtual void resetTransferedBytes(void);
private:
std::auto_ptr<CStreamPipe> bpipe;
BIO* bbio;
SSL_CTX* ctx;
bool has_error;
};
#endif //WITH_OPENSSL

View File

@ -59,6 +59,9 @@
#ifdef _WIN32
#include "SChannelPipe.h"
#endif
#ifdef WITH_OPENSSL
#include "OpenSSLPipe.h"
#endif
#ifdef _WIN32
#include <condition_variable>
@ -215,6 +218,9 @@ void CServer::setup(void)
#ifdef _WIN32
SChannelPipe::init();
#endif
#ifdef WITH_OPENSSL
OpenSSLPipe::init();
#endif
#ifdef ENABLE_C_ARES
LookupInit();
@ -1198,7 +1204,8 @@ IPipe* CServer::ConnectStream(const SLookupBlockingResult& lookup_result, unsign
IPipe * CServer::ConnectSslStream(const std::string & pServer, unsigned short pPort, unsigned int pTimeoutms)
{
#ifdef _WIN32
#if defined(_WIN32) || defined(WITH_OPENSSL)
int64 starttime = Server->getTimeMS();
CStreamPipe* bpipe = static_cast<CStreamPipe*>(ConnectStream(pServer, pPort, pTimeoutms));
@ -1208,7 +1215,11 @@ IPipe * CServer::ConnectSslStream(const std::string & pServer, unsigned short pP
int64 remaining_time = pTimeoutms - (Server->getTimeMS() - starttime);
if (remaining_time < 0) remaining_time = 0;
#ifdef _WIN32
SChannelPipe* ssl_pipe = new SChannelPipe(bpipe);
#else
OpenSSLPipe* ssl_pipe = new OpenSSLPipe(bpipe);
#endif
if (!ssl_pipe->ssl_connect(pServer, static_cast<int>(remaining_time)))
{

View File

@ -69,6 +69,33 @@ namespace
return rc;
}
int selectSocketReadWrite(SOCKET s, int timeoutms)
{
#ifdef _WIN32
fd_set conn;
FD_ZERO(&conn);
FD_SET(s, &conn);
timeval *tv = NULL;
timeval to;
if (timeoutms >= 0)
{
to.tv_sec = (long)(timeoutms / 1000);
to.tv_usec = (long)(timeoutms % 1000) * 1000;
tv = &to;
}
int rc = select((int)s + 1, &conn, &conn, NULL, tv);
#else
pollfd conn[1];
conn[0].fd = s;
conn[0].events = POLLIN|POLLOUT;
conn[0].revents = 0;
int rc = poll(conn, 1, timeoutms);
#endif
return rc;
}
int selectSocketWrite(SOCKET s, int timeoutms)
{
#ifdef _WIN32
@ -254,6 +281,31 @@ bool CStreamPipe::isReadable(int timeoutms)
}
}
bool CStreamPipe::isReadOrWritable(int timeoutms)
{
if (!doThrottle(0, false, false))
{
return false;
}
if (!doThrottle(0, true, false))
{
return false;
}
int rc = selectSocketReadWrite(s, timeoutms);
if (rc>0)
return true;
else
{
if (rc<0)
{
has_error = true;
}
return false;
}
}
bool CStreamPipe::hasError(void)
{
return has_error;

View File

@ -17,6 +17,7 @@ public:
virtual bool isWritable(int timeoutms);
virtual bool isReadable(int timeoutms);
virtual bool isReadOrWritable(int timeoutms);
virtual bool hasError(void);
@ -35,9 +36,10 @@ public:
virtual bool Flush( int timeoutms=-1 );
bool doThrottle(size_t new_bytes, bool outgoing, bool wait);
private:
SOCKET s;
bool doThrottle(size_t new_bytes, bool outgoing, bool wait);
_i64 transfered_bytes;

View File

@ -139,6 +139,9 @@ then
])
fi
AX_CHECK_OPENSSL([with_openssl=true], [with_openssl=false]
AM_CONDITIONAL([WITH_OPENSSL], [test x$with_openssl = xtrue])
AM_CONDITIONAL(WITH_GUI_CLIENT, test "x$enable_headless" != "xyes")
AS_IF([test "x$enable_headless" != "xyes"],

View File

@ -140,6 +140,21 @@ DLLEXPORT void LoadActions(IServer* pServer)
return;
}
std::string ssltest = Server->getServerParameter("ssltest");
if (!ssltest.empty())
{
IPipe* p = Server->ConnectSslStream("google.de", 443, 10000);
p->Write("GET / HTTP/1.0\r\n\r\n");
std::string ret;
while (p->Read(&ret) > 0)
{
Server->Log("SSL_OUT: " + ret);
}
return;
}
#ifdef _WIN32
char t_lang[20];
GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT,LOCALE_SISO639LANGNAME ,t_lang,sizeof(t_lang));