diff --git a/Connection.cpp b/Connection.cpp index 02879fef..9b6ed435 100644 --- a/Connection.cpp +++ b/Connection.cpp @@ -2,7 +2,7 @@ * This file is part of nzbget * * Copyright (C) 2004 Sven Henkel - * Copyright (C) 2007-2009 Andrey Prygunkov + * Copyright (C) 2007-2013 Andrey Prygunkov * * 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 @@ -54,18 +54,15 @@ #include "nzbget.h" #include "Connection.h" #include "Log.h" -#include "TLS.h" static const int CONNECTION_READBUFFER_SIZE = 1024; -#ifndef DISABLE_TLS -bool Connection::bTLSLibInitialized = false; -#endif #ifndef HAVE_GETADDRINFO #ifndef HAVE_GETHOSTBYNAME_R Mutex* Connection::m_pMutexGetHostByName = NULL; #endif #endif + void Connection::Init() { debug("Initializing global connection data"); @@ -87,18 +84,7 @@ void Connection::Init() #endif #ifndef DISABLE_TLS - debug("Initializing TLS library"); - char* szErrStr; - int iRes = tls_lib_init(&szErrStr); - bTLSLibInitialized = iRes == TLS_EOK; - if (!bTLSLibInitialized) - { - error("Could not initialize TLS library: %s", szErrStr ? szErrStr : "unknown error"); - if (szErrStr) - { - free(szErrStr); - } - } + TLSSocket::Init(); #endif #ifndef HAVE_GETADDRINFO @@ -117,11 +103,7 @@ void Connection::Final() #endif #ifndef DISABLE_TLS - if (bTLSLibInitialized) - { - debug("Finalizing TLS library"); - tls_lib_deinit(); - } + TLSSocket::Final(); #endif #ifndef HAVE_GETADDRINFO @@ -145,7 +127,7 @@ Connection::Connection(const char* szHost, int iPort, bool bTLS) m_bSuppressErrors = true; m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1); #ifndef DISABLE_TLS - m_pTLS = NULL; + m_pTLSSocket = NULL; m_bTLSError = false; #endif @@ -169,7 +151,7 @@ Connection::Connection(SOCKET iSocket, bool bTLS) m_bSuppressErrors = true; m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1); #ifndef DISABLE_TLS - m_pTLS = NULL; + m_pTLSSocket = NULL; m_bTLSError = false; #endif } @@ -186,9 +168,20 @@ Connection::~Connection() } free(m_szReadBuf); #ifndef DISABLE_TLS - if (m_pTLS) + if (m_pTLSSocket) { - free(m_pTLS); + delete m_pTLSSocket; + } +#endif +} + +void Connection::SetSuppressErrors(bool bSuppressErrors) +{ + m_bSuppressErrors = bSuppressErrors; +#ifndef DISABLE_TLS + if (m_pTLSSocket) + { + m_pTLSSocket->SetSuppressErrors(bSuppressErrors); } #endif } @@ -464,7 +457,7 @@ bool Connection::DoConnect() } #ifndef DISABLE_TLS - if (m_bTLS && !StartTLS()) + if (m_bTLS && !StartTLS(true, NULL, NULL)) { return false; } @@ -479,11 +472,11 @@ bool Connection::DoDisconnect() if (m_iSocket != INVALID_SOCKET) { - closesocket(m_iSocket); - m_iSocket = INVALID_SOCKET; #ifndef DISABLE_TLS CloseTLS(); #endif + closesocket(m_iSocket); + m_iSocket = INVALID_SOCKET; } m_eStatus = csDisconnected; @@ -664,7 +657,7 @@ int Connection::DoBind() return -1; } - if (listen(m_iSocket, 10) < 0) + if (listen(m_iSocket, 100) < 0) { ReportError("Listen on socket failed for %s", m_szHost, true, 0); return -1; @@ -763,73 +756,40 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool } #ifndef DISABLE_TLS -bool Connection::CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix) -{ - bool bOK = iResultCode == TLS_EOK; - if (!bOK) - { - ReportError(szErrMsgPrefix, szErrStr ? szErrStr : "unknown error", false, 0); - if (szErrStr) - { - free(szErrStr); - } - } - return bOK; -} - -bool Connection::StartTLS() +bool Connection::StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile) { debug("Starting TLS"); - if (m_pTLS) + if (m_pTLSSocket) { - free(m_pTLS); + delete m_pTLSSocket; } - m_pTLS = malloc(sizeof(tls_t)); - tls_t* pTLS = (tls_t*)m_pTLS; - memset(pTLS, 0, sizeof(tls_t)); + m_pTLSSocket = new TLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile); + m_pTLSSocket->SetSuppressErrors(m_bSuppressErrors); - char* szErrStr; - int iRes; - - iRes = tls_init(pTLS, NULL, NULL, NULL, 0, &szErrStr); - if (!CheckTLSResult(iRes, szErrStr, "Could not initialize secure connection: %s")) - { - return false; - } - - debug("tls_start..."); - iRes = tls_start(pTLS, (int)m_iSocket, NULL, 1, NULL, &szErrStr); - debug("tls_start...%i", iRes); - if (!CheckTLSResult(iRes, szErrStr, "Could not establish secure connection: %s")) - { - return false; - } - - return true; + return m_pTLSSocket->Start(); } void Connection::CloseTLS() { - if (m_pTLS) + if (m_pTLSSocket) { - tls_close((tls_t*)m_pTLS); - free(m_pTLS); - m_pTLS = NULL; + m_pTLSSocket->Close(); + delete m_pTLSSocket; + m_pTLSSocket = NULL; } } int Connection::recv(SOCKET s, char* buf, int len, int flags) { - size_t iReceived = 0; + int iReceived = 0; - if (m_pTLS) + if (m_pTLSSocket) { m_bTLSError = false; - char* szErrStr; - int iRes = tls_getbuf((tls_t*)m_pTLS, buf, len, &iReceived, &szErrStr); - if (!CheckTLSResult(iRes, szErrStr, "TLS-error: %s")) + iReceived = m_pTLSSocket->Recv(buf, len); + if (iReceived < 0) { m_bTLSError = true; return -1; @@ -844,22 +804,23 @@ int Connection::recv(SOCKET s, char* buf, int len, int flags) int Connection::send(SOCKET s, const char* buf, int len, int flags) { - if (m_pTLS) + int iSent = 0; + + if (m_pTLSSocket) { m_bTLSError = false; - char* szErrStr; - int iRes = tls_putbuf((tls_t*)m_pTLS, buf, len, &szErrStr); - if (!CheckTLSResult(iRes, szErrStr, "TLS-error: %s")) + iSent = m_pTLSSocket->Send(buf, len); + if (iSent < 0) { m_bTLSError = true; return -1; } - return len; + return iSent; } else { - int iRet = ::send(s, buf, len, flags); - return iRet; + iSent = ::send(s, buf, len, flags); + return iSent; } } #endif @@ -921,7 +882,7 @@ const char* Connection::GetRemoteAddr() if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (SOCKLEN_T*) &iPeerNameLength) >= 0) { #ifdef WIN32 - strncpy(m_szRemoteAddr, sizeof(m_szRemoteAddr), inet_ntoa(PeerName.sin_addr)); + strncpy(m_szRemoteAddr, inet_ntoa(PeerName.sin_addr), sizeof(m_szRemoteAddr)); #else inet_ntop(AF_INET, &PeerName.sin_addr, m_szRemoteAddr, sizeof(m_szRemoteAddr)); #endif diff --git a/Connection.h b/Connection.h index d647a097..31289325 100644 --- a/Connection.h +++ b/Connection.h @@ -2,7 +2,7 @@ * This file is part of nzbget * * Copyright (C) 2004 Sven Henkel - * Copyright (C) 2007-2009 Andrey Prygunkov + * Copyright (C) 2007-2013 Andrey Prygunkov * * 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 @@ -32,6 +32,9 @@ #include "Thread.h" #endif #endif +#ifndef DISABLE_TLS +#include "TLS.h" +#endif class Connection { @@ -44,8 +47,6 @@ public: csCancelled }; - typedef void TLS; - protected: char* m_szHost; int m_iPort; @@ -59,8 +60,7 @@ protected: bool m_bSuppressErrors; char m_szRemoteAddr[20]; #ifndef DISABLE_TLS - TLS* m_pTLS; - static bool bTLSLibInitialized; + TLSSocket* m_pTLSSocket; bool m_bTLSError; #endif #ifndef HAVE_GETADDRINFO @@ -81,7 +81,6 @@ protected: unsigned int ResolveHostAddr(const char* szHost); #endif #ifndef DISABLE_TLS - bool CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix); int recv(SOCKET s, char* buf, int len, int flags); int send(SOCKET s, const char* buf, int len, int flags); void CloseTLS(); @@ -108,11 +107,11 @@ public: bool GetTLS() { return m_bTLS; } void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; } EStatus GetStatus() { return m_eStatus; } - void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; } + void SetSuppressErrors(bool bSuppressErrors); bool GetSuppressErrors() { return m_bSuppressErrors; } const char* GetRemoteAddr(); #ifndef DISABLE_TLS - bool StartTLS(); + bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile); #endif }; diff --git a/Options.cpp b/Options.cpp index c8408b11..4ac1aa2a 100644 --- a/Options.cpp +++ b/Options.cpp @@ -114,6 +114,10 @@ static const char* OPTION_RENAMEBROKEN = "RenameBroken"; static const char* OPTION_CONTROLIP = "ControlIp"; static const char* OPTION_CONTROLPORT = "ControlPort"; static const char* OPTION_CONTROLPASSWORD = "ControlPassword"; +static const char* OPTION_SECURECONTROL = "SecureControl"; +static const char* OPTION_SECUREPORT = "SecurePort"; +static const char* OPTION_SECURECERT = "SecureCert"; +static const char* OPTION_SECUREKEY = "SecureKey"; static const char* OPTION_CONNECTIONTIMEOUT = "ConnectionTimeout"; static const char* OPTION_SAVEQUEUE = "SaveQueue"; static const char* OPTION_RELOADQUEUE = "ReloadQueue"; @@ -369,9 +373,13 @@ Options::Options(int argc, char* argv[]) m_bDupeCheck = false; m_iRetries = 0; m_iRetryInterval = 0; - m_szControlPort = 0; + m_iControlPort = 0; m_szControlIP = NULL; m_szControlPassword = NULL; + m_bSecureControl = false; + m_iSecurePort = 0; + m_szSecureCert = NULL; + m_szSecureKey = NULL; m_szLockFile = NULL; m_szDaemonUserName = NULL; m_eOutputMode = omLoggable; @@ -473,6 +481,7 @@ Options::Options(int argc, char* argv[]) } InitOptions(); + CheckOptions(); if (!m_bPrintOptions) { @@ -482,7 +491,6 @@ Options::Options(int argc, char* argv[]) InitServers(); InitCategories(); InitScheduler(); - CheckOptions(); if (m_bPrintOptions) { @@ -550,6 +558,14 @@ Options::~Options() { free(m_szControlPassword); } + if (m_szSecureCert) + { + free(m_szSecureCert); + } + if (m_szSecureKey) + { + free(m_szSecureKey); + } if (m_szLogFile) { free(m_szLogFile); @@ -656,6 +672,10 @@ void Options::InitDefault() SetOption(OPTION_CONTROLIP, "0.0.0.0"); SetOption(OPTION_CONTROLPASSWORD, "tegbzn6789"); SetOption(OPTION_CONTROLPORT, "6789"); + SetOption(OPTION_SECURECONTROL, "no"); + SetOption(OPTION_SECUREPORT, "6791"); + SetOption(OPTION_SECURECERT, ""); + SetOption(OPTION_SECUREKEY, ""); SetOption(OPTION_CONNECTIONTIMEOUT, "60"); SetOption(OPTION_SAVEQUEUE, "yes"); SetOption(OPTION_RELOADQUEUE, "yes"); @@ -832,6 +852,8 @@ void Options::InitOptions() m_szNZBAddedProcess = strdup(GetOption(OPTION_NZBADDEDPROCESS)); m_szControlIP = strdup(GetOption(OPTION_CONTROLIP)); m_szControlPassword = strdup(GetOption(OPTION_CONTROLPASSWORD)); + m_szSecureCert = strdup(GetOption(OPTION_SECURECERT)); + m_szSecureKey = strdup(GetOption(OPTION_SECUREKEY)); m_szLockFile = strdup(GetOption(OPTION_LOCKFILE)); m_szDaemonUserName = strdup(GetOption(OPTION_DAEMONUSERNAME)); m_szLogFile = strdup(GetOption(OPTION_LOGFILE)); @@ -841,7 +863,8 @@ void Options::InitOptions() m_iTerminateTimeout = ParseIntValue(OPTION_TERMINATETIMEOUT, 10); m_iRetries = ParseIntValue(OPTION_RETRIES, 10); m_iRetryInterval = ParseIntValue(OPTION_RETRYINTERVAL, 10); - m_szControlPort = ParseIntValue(OPTION_CONTROLPORT, 10); + m_iControlPort = ParseIntValue(OPTION_CONTROLPORT, 10); + m_iSecurePort = ParseIntValue(OPTION_SECUREPORT, 10); m_iUrlConnections = ParseIntValue(OPTION_URLCONNECTIONS, 10); m_iLogBufferSize = ParseIntValue(OPTION_LOGBUFFERSIZE, 10); m_iUMask = ParseIntValue(OPTION_UMASK, 8); @@ -887,6 +910,7 @@ void Options::InitOptions() m_bDeleteCleanupDisk = (bool)ParseEnumValue(OPTION_DELETECLEANUPDISK, BoolCount, BoolNames, BoolValues); m_bMergeNzb = (bool)ParseEnumValue(OPTION_MERGENZB, BoolCount, BoolNames, BoolValues); m_bAccurateRate = (bool)ParseEnumValue(OPTION_ACCURATERATE, BoolCount, BoolNames, BoolValues); + m_bSecureControl = (bool)ParseEnumValue(OPTION_SECURECONTROL, BoolCount, BoolNames, BoolValues); const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" }; const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses }; @@ -2301,6 +2325,14 @@ void Options::CheckOptions() } #endif +#ifdef DISABLE_TLS + if (m_bSecureControl) + { + LocateOptionSrcPos(OPTION_SECURECONTROL); + ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", OPTION_SECURECONTROL); + } +#endif + if (!m_bDecode) { m_bDirectWrite = false; diff --git a/Options.h b/Options.h index ce0014e5..a0fe2cb1 100644 --- a/Options.h +++ b/Options.h @@ -203,7 +203,11 @@ private: bool m_bDupeCheck; char* m_szControlIP; char* m_szControlPassword; - int m_szControlPort; + int m_iControlPort; + bool m_bSecureControl; + int m_iSecurePort; + char* m_szSecureCert; + char* m_szSecureKey; char* m_szLockFile; char* m_szDaemonUserName; EOutputMode m_eOutputMode; @@ -349,7 +353,11 @@ public: bool GetDupeCheck() { return m_bDupeCheck; } const char* GetControlIP() { return m_szControlIP; } const char* GetControlPassword() { return m_szControlPassword; } - int GetControlPort() { return m_szControlPort; } + int GetControlPort() { return m_iControlPort; } + bool GetSecureControl() { return m_bSecureControl; } + int GetSecurePort() { return m_iSecurePort; } + const char* GetSecureCert() { return m_szSecureCert; } + const char* GetSecureKey() { return m_szSecureKey; } const char* GetLockFile() { return m_szLockFile; } const char* GetDaemonUserName() { return m_szDaemonUserName; } EOutputMode GetOutputMode() { return m_eOutputMode; } diff --git a/README b/README index d8d0c5cc..1ce7fbf2 100644 --- a/README +++ b/README @@ -505,9 +505,6 @@ Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005. In 2007 the abandoned project was overtaken by Andrey Prygunkov. Since then the program has been completely rewritten. -Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers -(marlam@marlam.de). - ===================================== 9. Copyright ===================================== diff --git a/RemoteServer.cpp b/RemoteServer.cpp index 66bf999d..51eb2528 100644 --- a/RemoteServer.cpp +++ b/RemoteServer.cpp @@ -2,7 +2,7 @@ * This file is part of nzbget * * Copyright (C) 2005 Bo Cordes Petersen - * Copyright (C) 2007-2009 Andrey Prygunkov + * Copyright (C) 2007-2013 Andrey Prygunkov * * 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 @@ -54,10 +54,11 @@ extern Options* g_pOptions; //***************************************************************** // RemoteServer -RemoteServer::RemoteServer() +RemoteServer::RemoteServer(bool bTLS) { debug("Creating RemoteServer"); + m_bTLS = bTLS; m_pConnection = NULL; } @@ -75,13 +76,32 @@ void RemoteServer::Run() { debug("Entering RemoteServer-loop"); +#ifndef DISABLE_TLS + if (m_bTLS) + { + if (strlen(g_pOptions->GetSecureCert()) == 0 || !Util::FileExists(g_pOptions->GetSecureCert())) + { + error("Could not initialize TLS, secure certificate is not configured or the cert-file was not found. Check option "); + return; + } + + if (strlen(g_pOptions->GetSecureKey()) == 0 || !Util::FileExists(g_pOptions->GetSecureKey())) + { + error("Could not initialize TLS, secure key is not configured or the key-file was not found. Check option "); + return; + } + } +#endif + while (!IsStopped()) { bool bBind = true; if (!m_pConnection) { - m_pConnection = new Connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false); + m_pConnection = new Connection(g_pOptions->GetControlIP(), + m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), + m_bTLS); m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout()); m_pConnection->SetSuppressErrors(false); bBind = m_pConnection->Bind() == 0; @@ -109,8 +129,12 @@ void RemoteServer::Run() RequestProcessor* commandThread = new RequestProcessor(); commandThread->SetAutoDestroy(true); commandThread->SetConnection(pAcceptedConnection); +#ifndef DISABLE_TLS + commandThread->SetTLS(m_bTLS); +#endif commandThread->Start(); } + if (m_pConnection) { m_pConnection->Disconnect(); @@ -145,11 +169,24 @@ void RequestProcessor::Run() { bool bOK = false; + m_pConnection->SetSuppressErrors(true); + +#ifndef DISABLE_TLS + if (m_bTLS && !m_pConnection->StartTLS(false, g_pOptions->GetSecureCert(), g_pOptions->GetSecureKey())) + { + debug("Could not establish secure connection to web-client: Start TLS failed"); + return; + } +#endif + // Read the first 4 bytes to determine request type int iSignature = 0; if (!m_pConnection->Recv((char*)&iSignature, 4)) { - warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr()); + if (!m_bTLS) + { + warn("Non-nzbget request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr()); + } return; } @@ -199,6 +236,6 @@ void RequestProcessor::Run() if (!bOK) { - warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr()); + warn("Non-nzbget request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr()); } } diff --git a/RemoteServer.h b/RemoteServer.h index 3f34d9bf..26e4bb3e 100644 --- a/RemoteServer.h +++ b/RemoteServer.h @@ -1,8 +1,8 @@ /* * This file is part of nzbget * - * Copyright (C) 2005 Bo Cordes Petersen - * Copyright (C) 2007 Andrey Prygunkov + * Copyright (C) 2005 Bo Cordes Petersen + * Copyright (C) 2007-2013 Andrey Prygunkov * * 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 @@ -33,10 +33,11 @@ class RemoteServer : public Thread { private: + bool m_bTLS; Connection* m_pConnection; public: - RemoteServer(); + RemoteServer(bool bTLS); ~RemoteServer(); virtual void Run(); virtual void Stop(); @@ -45,11 +46,13 @@ public: class RequestProcessor : public Thread { private: + bool m_bTLS; Connection* m_pConnection; public: ~RequestProcessor(); virtual void Run(); + void SetTLS(bool bTLS) { m_bTLS = bTLS; } void SetConnection(Connection* pConnection) { m_pConnection = pConnection; } }; diff --git a/TLS.cpp b/TLS.cpp index 5db45248..06f9e4d9 100644 --- a/TLS.cpp +++ b/TLS.cpp @@ -1,13 +1,7 @@ /* * This file is part of nzbget * - * Based on "tls.c" from project "mpop" by Martin Lambers - * Original source code available on http://sourceforge.net/projects/mpop/ - * - * Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007 - * Martin Lambers - * - * Copyright (C) 2008-2009 Andrey Prygunkov + * Copyright (C) 2008-2013 Andrey Prygunkov * * 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 @@ -34,8 +28,8 @@ #ifndef DISABLE_TLS -#define SKIP_DEFAULT_WINDOWS_HEADERS #ifdef WIN32 +#define SKIP_DEFAULT_WINDOWS_HEADERS #include "win32.h" #endif @@ -61,52 +55,29 @@ #ifdef HAVE_LIBGNUTLS #include -#include +#if GNUTLS_VERSION_NUMBER <= 0x020b00 +#define NEED_GCRYPT_LOCKING +#endif +#ifdef NEED_GCRYPT_LOCKING #include +#endif /* NEED_GCRYPT_LOCKING */ #endif /* HAVE_LIBGNUTLS */ #ifdef HAVE_OPENSSL #include -#include -#include #include -#include -#include #endif /* HAVE_OPENSSL */ -#ifdef HAVE_LIBIDN -# include -#endif - #ifndef WIN32 #include "nzbget.h" #endif #include "TLS.h" #include "Thread.h" - -/** - * Substitutes for xasprintf, xmalloc, xstrdup and _() to remove dependencies from gnulib - */ -#define xmalloc (char*)malloc -#define xstrdup strdup -#define _(a) (a) - -char* xasprintf(const char* msg, ...) -{ - char* szResult = (char*)malloc(2048); // should be enough for all messages - - va_list ap; - va_start(ap, msg); - vsnprintf(szResult, 2048, msg, ap); - szResult[2048-1] = '\0'; - va_end(ap); - - return szResult; -} -/* End Substitutes */ +#include "Log.h" #ifdef HAVE_LIBGNUTLS +#ifdef NEED_GCRYPT_LOCKING /** * Mutexes for gcryptlib @@ -144,1428 +115,416 @@ static int gcry_mutex_unlock(void **lock) } static struct gcry_thread_cbs gcry_threads_Mutex = -{ GCRY_THREAD_OPTION_USER, NULL, - gcry_mutex_init, gcry_mutex_destroy, - gcry_mutex_lock, gcry_mutex_unlock, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +{ GCRY_THREAD_OPTION_USER, NULL, + gcry_mutex_init, gcry_mutex_destroy, + gcry_mutex_lock, gcry_mutex_unlock, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; +#endif /* NEED_GCRYPT_LOCKING */ #endif /* HAVE_LIBGNUTLS */ -/* - * tls_clear() - * - * see tls.h - */ - -void tls_clear(tls_t *tls) -{ - tls->have_trust_file = 0; - tls->is_active = 0; -} - - -/* - * seed_prng() - * - * Seeds the OpenSSL random number generator. - * Used error codes: TLS_ESEED - */ #ifdef HAVE_OPENSSL -int seed_prng(char **errstr) -{ - char randfile[512]; - time_t t; - int prn; - int system_prn_max = 1024; - /* Most systems have /dev/random or other sources of random numbers that - * OpenSSL can use to seed itself. - * The only system I know of where we must seed the PRNG is DOS. - */ - if (!RAND_status()) +/** + * Mutexes for OpenSSL + */ + +Mutex* *g_pOpenSSLMutexes; + +static void openssl_locking(int mode, int n, const char *file, int line) +{ + Mutex* mutex = g_pOpenSSLMutexes[n]; + if (mode & CRYPTO_LOCK) { - if (!RAND_file_name(randfile, 512)) - { - *errstr = xasprintf(_("no environment variables RANDFILE or HOME, " - "or filename of rand file too long")); - return TLS_ESEED; - } - if (RAND_load_file(randfile, -1) < 1) - { - *errstr = xasprintf(_("%s: input error"), randfile); - return TLS_ESEED; - } - /* Seed in time. I can't think of other "random" things on DOS - * systems. */ - if ((t = time(NULL)) < 0) - { - *errstr = xasprintf(_("cannot get system time: %s"), - strerror(errno)); - return TLS_ESEED; - } - RAND_seed((unsigned char *)&t, sizeof(time_t)); - /* If the RANDFILE + time is not enough, we fall back to the insecure - * and stupid method of seeding OpenSSLs PRNG with the systems PRNG. */ - if (!RAND_status()) - { - srand((unsigned int)(t % UINT_MAX)); - while (!RAND_status() && system_prn_max > 0) - { - prn = rand(); - RAND_seed(&prn, sizeof(int)); - system_prn_max--; - } - } - /* Are we happy now? */ - if (!RAND_status()) - { - *errstr = xasprintf(_("random file + time + pseudo randomness is " - "not enough, giving up")); - return TLS_ESEED; - } - /* Save a rand file for later usage. We ignore errors here as we can't - * do anything about them. */ - (void)RAND_write_file(randfile); + mutex->Lock(); + } + else + { + mutex->Unlock(); } - return TLS_EOK; } + +/* +static unsigned long openssl_thread_id(void) +{ +#ifdef WIN32 + return (unsigned long)GetCurrentThreadId(); +#else + return (unsigned long)pthread_self(); +#endif +} +*/ + +static struct CRYPTO_dynlock_value* openssl_dynlock_create(const char *file, int line) +{ + return (CRYPTO_dynlock_value*)new Mutex(); +} + +static void openssl_dynlock_destroy(struct CRYPTO_dynlock_value *l, const char *file, int line) +{ + Mutex* mutex = (Mutex*)l; + delete mutex; +} + +static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line) +{ + Mutex* mutex = (Mutex*)l; + if (mode & CRYPTO_LOCK) + { + mutex->Lock(); + } + else + { + mutex->Unlock(); + } +} + #endif /* HAVE_OPENSSL */ -/* - * tls_lib_init() - * - * see tls.h - */ - -int tls_lib_init(char **errstr) +void TLSSocket::Init() { + debug("Initializing TLS library"); + #ifdef HAVE_LIBGNUTLS +#ifdef NEED_GCRYPT_LOCKING + g_pGCryptLibMutexes = new Mutexes(); +#endif /* NEED_GCRYPT_LOCKING */ + int error_code; - g_pGCryptLibMutexes = new Mutexes(); - if ((error_code = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_Mutex)) != 0) +#ifdef NEED_GCRYPT_LOCKING + error_code = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_Mutex); + if (error_code != 0) { - *errstr = xasprintf("%s", "Could not initialize libcrypt"); - return TLS_ELIBFAILED; + error("Could not initialize libcrypt"); + return; + } +#endif /* NEED_GCRYPT_LOCKING */ + + error_code = gnutls_global_init(); + if (error_code != 0) + { + error("Could not initialize libgnutls"); + return; } - if ((error_code = gnutls_global_init()) != 0) - { - *errstr = xasprintf("%s", gnutls_strerror(error_code)); - return TLS_ELIBFAILED; - } - - return TLS_EOK; #endif /* HAVE_LIBGNUTLS */ #ifdef HAVE_OPENSSL - int e; - + int iMaxMutexes = CRYPTO_num_locks(); + g_pOpenSSLMutexes = (Mutex**)malloc(sizeof(Mutex*)*iMaxMutexes); + for (int i=0; i < iMaxMutexes; i++) + { + g_pOpenSSLMutexes[i] = new Mutex(); + } + SSL_load_error_strings(); SSL_library_init(); - if ((e = seed_prng(errstr)) != TLS_EOK) - { - return e; - } + OpenSSL_add_all_algorithms(); - return TLS_EOK; + CRYPTO_set_locking_callback(openssl_locking); + //CRYPTO_set_id_callback(openssl_thread_id); + CRYPTO_set_dynlock_create_callback(openssl_dynlock_create); + CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy); + CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock); + #endif /* HAVE_OPENSSL */ } - -/* - * tls_is_active() - * - * see tls.h - */ - -int tls_is_active(tls_t *tls) +void TLSSocket::Final() { - return tls->is_active; -} + debug("Finalizing TLS library"); - -/* - * tls_cert_info_new() - */ - -tls_cert_info_t *tls_cert_info_new(void) -{ - tls_cert_info_t *tci; - int i; - - tci = (tls_cert_info_t*)xmalloc(sizeof(tls_cert_info_t)); - for (i = 0; i < 6; i++) - { - tci->owner_info[i] = NULL; - tci->issuer_info[i] = NULL; - } - - return tci; -} - - -/* - * tls_cert_info_free() - */ - -void tls_cert_info_free(tls_cert_info_t *tci) -{ - int i; - - if (tci) - { - for (i = 0; i < 6; i++) - { - free(tci->owner_info[i]); - free(tci->issuer_info[i]); - } - free(tci); - } -} - - -/* - * asn1time_to_time_t() [OpenSSL only] - * - * Convert a ASN1 time string ([YY]YYMMDDhhmm[ss](Z)) into a time_t. - * The flag 'is_utc' indicates whether the string is in UTC or GENERALIZED - * format. GENERALIZED means a 4 digit year. - * In case of invalid strings or over-/underflows, 1 is returned, and the value - * of 't' is undefined. On success, 0 is returned. - * - * This code uses many ideas from GnuTLS code (lib/x509/common.c). - * The transformation of struct tm to time_t is based on code from Russ Allbery - * (rra@stanford.edu), who wrote a mktime_utc function and placed it under - * public domain. - */ - -#ifdef HAVE_OPENSSL -int is_leap(int year) -{ - return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)); -} - -int asn1time_to_time_t(const char *asn1time, int is_utc, time_t *t) -{ - size_t len; - int i; - size_t j; - const char *p; - char xx[3]; - char xxxx[5]; - const int monthdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - struct tm tm; - - len = strlen(asn1time); - if ((is_utc && len < 10) || (!is_utc && len < 12)) - { - goto error_exit; - } - for (j = 0; j < len - 1; j++) - { - if (!isdigit((unsigned char)asn1time[j])) - { - goto error_exit; - } - } - - xx[2] = '\0'; - xxxx[4] = '\0'; - p = asn1time; - if (is_utc) - { - strncpy(xx, p, 2); - tm.tm_year = atoi(xx); - tm.tm_year += (tm.tm_year > 49) ? 1900 : 2000; - p += 2; - } - else - { - strncpy(xxxx, p, 4); - tm.tm_year = atoi(xxxx); - p += 4; - } - strncpy(xx, p, 2); - tm.tm_mon = atoi(xx) - 1; - p += 2; - strncpy(xx, p, 2); - tm.tm_mday = atoi(xx); - p += 2; - strncpy(xx, p, 2); - tm.tm_hour = atoi(xx); - p += 2; - strncpy(xx, p, 2); - tm.tm_min = atoi(xx); - p += 2; - if (isdigit((unsigned char)(*p))) - { - strncpy(xx, p, 2); - tm.tm_sec = atoi(xx); - } - else - { - tm.tm_sec = 0; - } - - /* basic check for 32 bit time_t overflows. */ - if (sizeof(time_t) <= 4 && tm.tm_year >= 2038) - { - goto error_exit; - } - if (tm.tm_year < 1970 || tm.tm_mon < 0 || tm.tm_mon > 11) - { - goto error_exit; - } - *t = 0; - for (i = 1970; i < tm.tm_year; i++) - { - *t += 365 + (is_leap(i) ? 1 : 0); - } - for (i = 0; i < tm.tm_mon; i++) - { - *t += monthdays[i]; - } - if (tm.tm_mon > 1 && is_leap(tm.tm_year)) - { - *t += 1; - } - *t = 24 * (*t + tm.tm_mday - 1) + tm.tm_hour; - *t = 60 * (*t) + tm.tm_min; - *t = 60 * (*t) + tm.tm_sec; - - return 0; - -error_exit: - return 1; -} -#endif /* HAVE_OPENSSL */ - - -/* - * tls_cert_info_get() - * - * see tls.h - */ - -int tls_cert_info_get(tls_t *tls, tls_cert_info_t *tci, char **errstr) -{ -#ifdef HAVE_LIBGNUTLS - const gnutls_datum_t *cert_list; - unsigned int cert_list_size; - gnutls_x509_crt_t cert; - size_t size; - const char *oid[6] = { GNUTLS_OID_X520_COMMON_NAME, - GNUTLS_OID_X520_ORGANIZATION_NAME, - GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, - GNUTLS_OID_X520_LOCALITY_NAME, - GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, - GNUTLS_OID_X520_COUNTRY_NAME }; - int i; - int e; - char *p; - const char *errmsg; - - errmsg = _("cannot get TLS certificate info"); - if (!(cert_list = - gnutls_certificate_get_peers(tls->session, &cert_list_size)) - || cert_list_size == 0) - { - *errstr = xasprintf(_("%s: no certificate was found"), errmsg); - return TLS_ECERT; - } - if (gnutls_x509_crt_init(&cert) != 0) - { - *errstr = xasprintf(_("%s: cannot initialize certificate structure"), - errmsg); - return TLS_ECERT; - } - if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) != 0) - { - *errstr = xasprintf(_("%s: error parsing certificate"), errmsg); - gnutls_x509_crt_deinit(cert); - return TLS_ECERT; - } - - /* certificate information */ - size = 20; - if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA, - tci->sha1_fingerprint, &size) != 0) - { - *errstr = xasprintf(_("%s: error getting SHA1 fingerprint"), errmsg); - gnutls_x509_crt_deinit(cert); - return TLS_ECERT; - } - size = 16; - if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, - tci->md5_fingerprint, &size) != 0) - { - *errstr = xasprintf(_("%s: error getting MD5 fingerprint"), errmsg); - gnutls_x509_crt_deinit(cert); - return TLS_ECERT; - } - if ((tci->activation_time = gnutls_x509_crt_get_activation_time(cert)) < 0) - { - *errstr = xasprintf(_("%s: cannot get activation time"), errmsg); - gnutls_x509_crt_deinit(cert); - return TLS_ECERT; - } - if ((tci->expiration_time = gnutls_x509_crt_get_expiration_time(cert)) < 0) - { - *errstr = xasprintf(_("%s: cannot get expiration time"), errmsg); - gnutls_x509_crt_deinit(cert); - return TLS_ECERT; - } - - /* owner information */ - for (i = 0; i < 6; i++) - { - size = 0; - e = gnutls_x509_crt_get_dn_by_oid(cert, oid[i], 0, 0, NULL, &size); - if (e == GNUTLS_E_SHORT_MEMORY_BUFFER) - { - p = xmalloc(size); - e = gnutls_x509_crt_get_dn_by_oid(cert, oid[i], 0, 0, p, &size); - if (e == 0) - { - tci->owner_info[i] = p; - } - else - { - free(p); - } - } - } - - /* issuer information */ - for (i = 0; i < 6; i++) - { - size = 0; - e = gnutls_x509_crt_get_issuer_dn_by_oid( - cert, oid[i], 0, 0, NULL, &size); - if (e == GNUTLS_E_SHORT_MEMORY_BUFFER) - { - p = xmalloc(size); - e = gnutls_x509_crt_get_issuer_dn_by_oid( - cert, oid[i], 0, 0, p, &size); - if (e == 0) - { - tci->issuer_info[i] = p; - } - else - { - free(p); - } - } - } - - gnutls_x509_crt_deinit(cert); - return TLS_EOK; -#endif /* HAVE_LIBGNUTLS */ - -#ifdef HAVE_OPENSSL - X509 *x509cert; - X509_NAME *x509_subject; - X509_NAME *x509_issuer; - ASN1_TIME *asn1time; - int nid[6] = { NID_commonName, - NID_organizationName, - NID_organizationalUnitName, - NID_localityName, - NID_stateOrProvinceName, - NID_countryName }; - int size; - unsigned int usize; - char *p; - int i; - const char *errmsg; - - errmsg = _("cannot get TLS certificate info"); - if (!(x509cert = SSL_get_peer_certificate(tls->ssl))) - { - *errstr = xasprintf(_("%s: no certificate was found"), errmsg); - return TLS_ECERT; - } - if (!(x509_subject = X509_get_subject_name(x509cert))) - { - *errstr = xasprintf(_("%s: cannot get certificate subject"), errmsg); - X509_free(x509cert); - return TLS_ECERT; - } - if (!(x509_issuer = X509_get_issuer_name(x509cert))) - { - *errstr = xasprintf(_("%s: cannot get certificate issuer"), errmsg); - X509_free(x509cert); - return TLS_ECERT; - } - - /* certificate information */ - usize = 20; - if (!X509_digest(x509cert, EVP_sha1(), tci->sha1_fingerprint, &usize)) - { - *errstr = xasprintf(_("%s: error getting SHA1 fingerprint"), errmsg); - return TLS_ECERT; - } - usize = 16; - if (!X509_digest(x509cert, EVP_md5(), tci->md5_fingerprint, &usize)) - { - *errstr = xasprintf(_("%s: error getting MD5 fingerprint"), errmsg); - return TLS_ECERT; - } - asn1time = X509_get_notBefore(x509cert); - if (asn1time_to_time_t((char *)asn1time->data, - (asn1time->type != V_ASN1_GENERALIZEDTIME), - &(tci->activation_time)) != 0) - { - *errstr = xasprintf(_("%s: cannot get activation time"), errmsg); - X509_free(x509cert); - tls_cert_info_free(tci); - return TLS_ECERT; - } - asn1time = X509_get_notAfter(x509cert); - if (asn1time_to_time_t((char *)asn1time->data, - (asn1time->type != V_ASN1_GENERALIZEDTIME), - &(tci->expiration_time)) != 0) - { - *errstr = xasprintf(_("%s: cannot get expiration time"), errmsg); - X509_free(x509cert); - tls_cert_info_free(tci); - return TLS_ECERT; - } - - /* owner information */ - for (i = 0; i < 6; i++) - { - size = X509_NAME_get_text_by_NID(x509_subject, nid[i], NULL, 0); - size++; - p = xmalloc((size_t)size); - if (X509_NAME_get_text_by_NID(x509_subject, nid[i], p, size) != -1) - { - tci->owner_info[i] = p; - } - else - { - free(p); - } - } - - /* issuer information */ - for (i = 0; i < 6; i++) - { - size = X509_NAME_get_text_by_NID(x509_issuer, nid[i], NULL, 0); - size++; - p = xmalloc((size_t)size); - if (X509_NAME_get_text_by_NID(x509_issuer, nid[i], p, size) != -1) - { - tci->issuer_info[i] = p; - } - else - { - free(p); - } - } - - X509_free(x509cert); - return TLS_EOK; -#endif /* HAVE_OPENSSL */ -} - - -/* - * [OpenSSL only] hostname_match() - * - * Compares the hostname with the name in the certificate. The certificate name - * may include a wildcard as the leftmost domain component (its first two - * characters are "*." in this case). - * - * Returns 1 if the names match, 0 otherwise. - * - * This is the same form of matching that gnutls_x509_crt_check_hostname() from - * GnuTLS 1.2.0 uses. - * It conforms to RFC2595 (Using TLS with IMAP, POP3 and ACAP), section 2.4. - * RFC2818 (HTTP over TLS), section 3.1 says that `f*.com matches foo.com'. This - * function does not allow that. - * RFC3207 (SMTP Service Extension for Secure SMTP over Transport Layer - * Security), section 4.1 says nothing more than `A SMTP client would probably - * only want to authenticate an SMTP server whose server certificate has a - * domain name that is the domain name that the client thought it was connecting - * to'. - */ - -#ifdef HAVE_OPENSSL -int hostname_match(const char *hostname, const char *certname) -{ - const char *cmp1, *cmp2; - - if (strncmp(certname, "*.", 2) == 0) - { - cmp1 = certname + 2; - cmp2 = strchr(hostname, '.'); - if (!cmp2) - { - return 0; - } - else - { - cmp2++; - } - } - else - { - cmp1 = certname; - cmp2 = hostname; - } - - if (*cmp1 == '\0' || *cmp2 == '\0') - { - return 0; - } - - if (strcasecmp(cmp1, cmp2) != 0) - { - return 0; - } - - return 1; -} -#endif /* HAVE_OPENSSL */ - - -/* - * tls_check_cert() - * - * If the 'verify' flag is set, perform a real verification of the peer's - * certificate. If this succeeds, the connection can be considered secure. - * If the 'verify' flag is not set, perform only a few sanity checks of the - * peer's certificate. You cannot trust the connection when this succeeds. - * Used error codes: TLS_ECERT - */ - -int tls_check_cert(tls_t *tls, const char *hostname, int verify, char **errstr) -{ -#ifdef HAVE_LIBGNUTLS - int error_code; - const char *error_msg; - unsigned int status; - const gnutls_datum_t *cert_list; - unsigned int cert_list_size; - unsigned int i; - gnutls_x509_crt_t cert; - time_t t1, t2; -#ifdef HAVE_LIBIDN - char *hostname_ascii; -#endif - - if (verify) - { - error_msg = _("TLS certificate verification failed"); - } - else - { - error_msg = _("TLS certificate check failed"); - } - - /* If 'verify' is true, this function uses the trusted CAs in the - * credentials structure. So you must have installed one or more CA - * certificates. */ - gnutls_certificate_set_verify_flags(tls->cred, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - if ((error_code = gnutls_certificate_verify_peers2(tls->session, - &status)) != 0) - { - *errstr = xasprintf("%s: %s", error_msg, gnutls_strerror(error_code)); - return TLS_ECERT; - } - if (verify) - { - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - { - *errstr = xasprintf( - _("%s: the certificate hasn't got a known issuer"), - error_msg); - return TLS_ECERT; - } - if (status & GNUTLS_CERT_INVALID) - { - *errstr = xasprintf(_("%s: the certificate is not trusted"), - error_msg); - return TLS_ECERT; - } - } - if (status & GNUTLS_CERT_REVOKED) - { - *errstr = xasprintf(_("%s: the certificate has been revoked"), - error_msg); - return TLS_ECERT; - } - if (gnutls_certificate_type_get(tls->session) != GNUTLS_CRT_X509) - { - *errstr = xasprintf(_("%s: the certificate type is not X509"), - error_msg); - return TLS_ECERT; - } - if (!(cert_list = gnutls_certificate_get_peers( - tls->session, &cert_list_size))) - { - *errstr = xasprintf(_("%s: no certificate was found"), error_msg); - return TLS_ECERT; - } - /* Needed to check times: */ - if ((t1 = time(NULL)) < 0) - { - *errstr = xasprintf("%s: cannot get system time: %s", error_msg, - strerror(errno)); - return TLS_ECERT; - } - /* Check the certificate chain. All certificates in the chain must have - * valid activation/expiration times. The first certificate in the chain is - * the host's certificate; it must match the hostname. */ - for (i = 0; i < cert_list_size; i++) - { - if (gnutls_x509_crt_init(&cert) < 0) - { - *errstr = xasprintf( - _("%s: cannot initialize certificate structure"), - error_msg); - return TLS_ECERT; - } - if (gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER) - < 0) - { - *errstr = xasprintf(_("%s: error parsing certificate %u of %u"), - error_msg, i + 1, cert_list_size); - return TLS_ECERT; - } - /* Check hostname */ - if (i == 0) - { -#ifdef HAVE_LIBIDN - if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) == IDNA_SUCCESS) - { - if (!gnutls_x509_crt_check_hostname(cert, hostname_ascii)) - { - *errstr = xasprintf(_("%s: the certificate owner does not " - "match hostname %s"), error_msg, hostname); - free(hostname_ascii); - return TLS_ECERT; - } - free(hostname_ascii); - } - else -#endif - if (!gnutls_x509_crt_check_hostname(cert, hostname)) - { - *errstr = xasprintf(_("%s: the certificate owner does not " - "match hostname %s"), error_msg, hostname); - return TLS_ECERT; - } - } - /* Check certificate times */ - if ((t2 = gnutls_x509_crt_get_activation_time(cert)) < 0) - { - *errstr = xasprintf(_("%s: cannot get activation time for " - "certificate %u of %u"), - error_msg, i + 1, cert_list_size); - return TLS_ECERT; - } - if (t2 > t1) - { - *errstr = xasprintf( - _("%s: certificate %u of %u is not yet activated"), - error_msg, i + 1, cert_list_size); - return TLS_ECERT; - } - if ((t2 = gnutls_x509_crt_get_expiration_time(cert)) < 0) - { - *errstr = xasprintf(_("%s: cannot get expiration time for " - "certificate %u of %u"), - error_msg, i + 1, cert_list_size); - return TLS_ECERT; - } - if (t2 < t1) - { - *errstr = xasprintf(_("%s: certificate %u of %u has expired"), - error_msg, i + 1, cert_list_size); - return TLS_ECERT; - } - gnutls_x509_crt_deinit(cert); - } - - return TLS_EOK; -#endif /* HAVE_LIBGNUTLS */ - -#ifdef HAVE_OPENSSL - X509 *x509cert; - long status; - const char *error_msg; - int i; - /* hostname in ASCII format: */ - char *hostname_ascii; - /* needed to get the common name: */ - X509_NAME *x509_subject; - char *buf; - int bufsize; - /* needed to get the DNS subjectAltNames: */ - STACK_OF(GENERAL_NAME) *subj_alt_names; - int subj_alt_names_count; - GENERAL_NAME *subj_alt_name; - /* did we find a name matching hostname? */ - int match_found; - - - if (verify) - { - error_msg = _("TLS certificate verification failed"); - } - else - { - error_msg = _("TLS certificate check failed"); - } - - /* Get certificate */ - if (!(x509cert = SSL_get_peer_certificate(tls->ssl))) - { - *errstr = xasprintf(_("%s: no certificate was sent"), error_msg); - return TLS_ECERT; - } - - /* Get result of OpenSSL's default verify function */ - if ((status = SSL_get_verify_result(tls->ssl)) != X509_V_OK) - { - if (verify || (status != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY - && status != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT - && status != X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)) - { - *errstr = xasprintf("%s: %s", error_msg, - X509_verify_cert_error_string(status)); - X509_free(x509cert); - return TLS_ECERT; - } - } - - /* Check if 'hostname' matches the one of the subjectAltName extensions of - * type DNS or the Common Name (CN). */ - -#ifdef HAVE_LIBIDN - if (idna_to_ascii_lz(hostname, &hostname_ascii, 0) != IDNA_SUCCESS) - { - hostname_ascii = xstrdup(hostname); - } -#else - hostname_ascii = xstrdup(hostname); -#endif - - /* Try the DNS subjectAltNames. */ - match_found = 0; - if ((subj_alt_names = - (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL))) - { - subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names); - for (i = 0; i < subj_alt_names_count; i++) - { - subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); - if (subj_alt_name->type == GEN_DNS) - { - if ((match_found = hostname_match(hostname_ascii, - (char *)(subj_alt_name->d.ia5->data)))) - { - break; - } - } - } - } - if (!match_found) - { - /* Try the common name */ - if (!(x509_subject = X509_get_subject_name(x509cert))) - { - *errstr = xasprintf(_("%s: cannot get certificate subject"), - error_msg); - X509_free(x509cert); - return TLS_ECERT; - } - bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, - NULL, 0); - bufsize++; - buf = xmalloc((size_t)bufsize); - if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, - buf, bufsize) == -1) - { - *errstr = xasprintf(_("%s: cannot get certificate common name"), - error_msg); - X509_free(x509cert); - free(buf); - return TLS_ECERT; - } - match_found = hostname_match(hostname_ascii, buf); - free(buf); - } - X509_free(x509cert); - free(hostname_ascii); - - if (!match_found) - { - *errstr = xasprintf( - _("%s: the certificate owner does not match hostname %s"), - error_msg, hostname); - return TLS_ECERT; - } - - return TLS_EOK; -#endif /* HAVE_OPENSSL */ -} - - -/* - * tls_init() - * - * see tls.h - */ - -int tls_init(tls_t *tls, const char *key_file, const char *cert_file, - const char *trust_file, int force_sslv3, char **errstr) -{ -#ifdef HAVE_LIBGNUTLS - int error_code; - - if ((error_code = gnutls_init(&tls->session, GNUTLS_CLIENT)) != 0) - { - *errstr = xasprintf(_("cannot initialize TLS Session: %s"), - gnutls_strerror(error_code)); - return TLS_ELIBFAILED; - } - if ((error_code = gnutls_set_default_priority(tls->session)) != 0) - { - *errstr = xasprintf(_("cannot set priorities on TLS Session: %s"), - gnutls_strerror(error_code)); - gnutls_deinit(tls->session); - return TLS_ELIBFAILED; - } - if (force_sslv3) - { - const int force_sslv3_proto_prio[2] = { GNUTLS_SSL3, 0 }; - if ((error_code = gnutls_protocol_set_priority(tls->session, - force_sslv3_proto_prio)) != 0) - { - *errstr = xasprintf(_("cannot force SSLv3: %s"), - gnutls_strerror(error_code)); - gnutls_deinit(tls->session); - return TLS_ELIBFAILED; - } - } - if ((error_code = gnutls_certificate_allocate_credentials(&tls->cred)) < 0) - { - *errstr = xasprintf( - _("cannot allocate certificate for TLS Session: %s"), - gnutls_strerror(error_code)); - gnutls_deinit(tls->session); - return TLS_ELIBFAILED; - } - if (key_file && cert_file) - { - if ((error_code = gnutls_certificate_set_x509_key_file(tls->cred, - cert_file, key_file, GNUTLS_X509_FMT_PEM)) < 0) - { - *errstr = xasprintf(_("cannot set X509 key file %s and/or " - "X509 cert file %s for TLS Session: %s"), - key_file, cert_file, gnutls_strerror(error_code)); - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); - return TLS_EFILE; - } - } - if (trust_file) - { - if ((error_code = gnutls_certificate_set_x509_trust_file( - tls->cred, trust_file, GNUTLS_X509_FMT_PEM)) <= 0) - { - *errstr = xasprintf( - _("cannot set X509 trust file %s for TLS Session: %s"), - trust_file, gnutls_strerror(error_code)); - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); - return TLS_EFILE; - } - tls->have_trust_file = 1; - } - if ((error_code = gnutls_credentials_set(tls->session, - GNUTLS_CRD_CERTIFICATE, tls->cred)) < 0) - { - *errstr = xasprintf(_("cannot set credentials for TLS Session: %s"), - gnutls_strerror(error_code)); - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); - return TLS_ELIBFAILED; - } - return TLS_EOK; - -#endif /* HAVE_LIBGNUTLS */ - -#ifdef HAVE_OPENSSL - - #if OPENSSL_VERSION_NUMBER >= 0x10000000L - const SSL_METHOD *ssl_method = NULL; - #else - SSL_METHOD *ssl_method = NULL; - #endif - - ssl_method = force_sslv3 ? SSLv3_client_method() : SSLv23_client_method(); - if (!ssl_method) - { - *errstr = xasprintf(_("cannot set TLS method")); - return TLS_ELIBFAILED; - } - if (!(tls->ssl_ctx = SSL_CTX_new(ssl_method))) - { - *errstr = xasprintf(_("cannot create TLS context: %s"), - ERR_error_string(ERR_get_error(), NULL)); - return TLS_ELIBFAILED; - } - /* SSLv2 has known flaws. Disable it. */ - (void)SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv2); - if (key_file && cert_file) - { - if (SSL_CTX_use_PrivateKey_file( - tls->ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) - { - *errstr = xasprintf(_("cannot load key file %s: %s"), - key_file, ERR_error_string(ERR_get_error(), NULL)); - SSL_CTX_free(tls->ssl_ctx); - tls->ssl_ctx = NULL; - return TLS_EFILE; - } - if (SSL_CTX_use_certificate_chain_file(tls->ssl_ctx, cert_file) != 1) - { - *errstr = xasprintf(_("cannot load certificate file %s: %s"), - cert_file, ERR_error_string(ERR_get_error(), NULL)); - SSL_CTX_free(tls->ssl_ctx); - tls->ssl_ctx = NULL; - return TLS_EFILE; - } - } - if (trust_file) - { - if (SSL_CTX_load_verify_locations(tls->ssl_ctx, trust_file, NULL) != 1) - { - *errstr = xasprintf(_("cannot load trust file %s: %s"), - trust_file, ERR_error_string(ERR_get_error(), NULL)); - SSL_CTX_free(tls->ssl_ctx); - tls->ssl_ctx = NULL; - return TLS_EFILE; - } - tls->have_trust_file = 1; - } - if (!(tls->ssl = SSL_new(tls->ssl_ctx))) - { - *errstr = xasprintf(_("cannot create a TLS structure: %s"), - ERR_error_string(ERR_get_error(), NULL)); - SSL_CTX_free(tls->ssl_ctx); - tls->ssl_ctx = NULL; - return TLS_ELIBFAILED; - } - return TLS_EOK; - -#endif /* HAVE_OPENSSL */ -} - - -/* - * openssl_io_error() - * - * Used only internally by the OpenSSL code. - * - * Construct an error line according to 'error_code' (which originates from an - * SSL_read(), SSL_write() or SSL_connect() operation) and 'error_code2' (which - * originates from an SSL_get_error() call with 'error_code' as its argument). - * The line will read: "error_string: error_reason". 'error_string' is given by - * the calling function, this function finds out 'error_reason'. - * The resulting string will be returned in an allocated string. - * OpenSSL error strings are max 120 characters long according to - * ERR_error_string(3). - */ - -#ifdef HAVE_OPENSSL -char *openssl_io_error(int error_code, int error_code2, - const char *error_string) -{ - unsigned long error_code3; - const char *error_reason; - - switch (error_code2) - { - case SSL_ERROR_SYSCALL: - error_code3 = ERR_get_error(); - if (error_code3 == 0) - { - if (error_code == 0) - { - error_reason = _("a protocol violating EOF occured"); - } - else if (error_code == -1) - { - error_reason = strerror(errno); - } - else - { - error_reason = _("unknown error"); - } - } - else - { - error_reason = ERR_error_string(error_code3, NULL); - } - break; - - case SSL_ERROR_ZERO_RETURN: - error_reason = _("the connection was closed unexpectedly"); - break; - - case SSL_ERROR_SSL: - error_reason = ERR_error_string(ERR_get_error(), NULL); - break; - - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - error_reason = _("the operation timed out"); - break; - - default: - /* probably SSL_ERROR_NONE */ - error_reason = _("unknown error"); - break; - } - return xasprintf("%s: %s", error_string, error_reason); -} -#endif /* HAVE_OPENSSL */ - - -/* - * tls_start() - * - * see tls.h - */ - -int tls_start(tls_t *tls, int fd, const char *hostname, int no_certcheck, - tls_cert_info_t *tci, char **errstr) -{ -#ifdef HAVE_LIBGNUTLS - int error_code; - - gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr_t)(size_t)fd); - if ((error_code = gnutls_handshake(tls->session)) < 0) - { - if (error_code == GNUTLS_E_INTERRUPTED) - { - *errstr = xasprintf(_("operation aborted")); - } - else if (error_code == GNUTLS_E_AGAIN) - { - /* This error message makes more sense than what - * gnutls_strerror() would return. */ - *errstr = xasprintf(_("TLS handshake failed: %s"), - _("the operation timed out")); - } - else - { - *errstr = xasprintf(_("TLS handshake failed: %s"), - gnutls_strerror(error_code)); - } - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); - return TLS_EHANDSHAKE; - } - if (tci) - { - if ((error_code = tls_cert_info_get(tls, tci, errstr)) != TLS_EOK) - { - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); - return error_code; - } - } - if (!no_certcheck) - { - if ((error_code = tls_check_cert(tls, hostname, - tls->have_trust_file, errstr)) != TLS_EOK) - { - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); - return error_code; - } - } - tls->is_active = 1; - return TLS_EOK; -#endif /* HAVE_LIBGNUTLS */ - -#ifdef HAVE_OPENSSL - int error_code; - - if (!SSL_set_fd(tls->ssl, fd)) - { - *errstr = xasprintf(_("cannot set the file descriptor for TLS: %s"), - ERR_error_string(ERR_get_error(), NULL)); - SSL_free(tls->ssl); - SSL_CTX_free(tls->ssl_ctx); - return TLS_ELIBFAILED; - } - if ((error_code = SSL_connect(tls->ssl)) < 1) - { - if (errno == EINTR - && (SSL_get_error(tls->ssl, error_code) == SSL_ERROR_WANT_READ - || SSL_get_error(tls->ssl, error_code) - == SSL_ERROR_WANT_WRITE)) - { - *errstr = xasprintf(_("operation aborted")); - } - else - { - *errstr = openssl_io_error(error_code, - SSL_get_error(tls->ssl, error_code), - _("TLS handshake failed")); - } - SSL_free(tls->ssl); - SSL_CTX_free(tls->ssl_ctx); - return TLS_EIO; - } - if (tci) - { - if ((error_code = tls_cert_info_get(tls, tci, errstr)) != TLS_EOK) - { - SSL_free(tls->ssl); - SSL_CTX_free(tls->ssl_ctx); - return error_code; - } - } - if (!no_certcheck) - { - if ((error_code = tls_check_cert(tls, hostname, tls->have_trust_file, - errstr)) != TLS_EOK) - { - SSL_free(tls->ssl); - SSL_CTX_free(tls->ssl_ctx); - return error_code; - } - } - tls->is_active = 1; - return TLS_EOK; -#endif /* HAVE_OPENSSL */ -} - - -/* - * tls_getbuf() - * - * see tls.h - */ - -int tls_getbuf(tls_t *tls, char* s, size_t len, size_t* readlen, char **errstr) -{ -#ifdef HAVE_LIBGNUTLS - *readlen = 0; - ssize_t ret; - - ret = gnutls_record_recv(tls->session, s, len); - if (ret >= 0) - { - *readlen = ret; - return TLS_EOK; - } - else - { - if (ret == GNUTLS_E_INTERRUPTED) - { - *errstr = xasprintf(_("operation aborted")); - } - else if (ret == GNUTLS_E_AGAIN) - { - /* This error message makes more sense than what - * gnutls_strerror() would return. */ - *errstr = xasprintf(_("cannot read from TLS connection: %s"), - _("the operation timed out")); - } - else - { - *errstr = xasprintf(_("cannot read from TLS connection: %s"), - gnutls_strerror((int)ret)); - } - return TLS_EIO; - } - -#endif /* HAVE_LIBGNUTLS */ - -#ifdef HAVE_OPENSSL - *readlen = 0; - - int error_code; - int error_code2; - - int ret = SSL_read(tls->ssl, s, len); - - if ((error_code = ret) < 1) - { - if ((error_code2 = SSL_get_error(tls->ssl, error_code)) - == SSL_ERROR_NONE) - { - return TLS_EOK; - } - else - { - if (errno == EINTR && - (SSL_get_error(tls->ssl, error_code) == SSL_ERROR_WANT_READ - || SSL_get_error(tls->ssl, error_code) - == SSL_ERROR_WANT_WRITE)) - { - *errstr = xasprintf(_("operation aborted")); - } - else - { - *errstr = openssl_io_error(error_code, error_code2, - _("cannot read from TLS connection")); - } - return TLS_EIO; - } - } - else - { - *readlen = ret; - return TLS_EOK; - } - -#endif /* HAVE_OPENSSL */ -} - - -/* - * tls_putbuf() - * - * see tls.h - */ - -int tls_putbuf(tls_t *tls, const char *s, size_t len, char **errstr) -{ -#ifdef HAVE_LIBGNUTLS - - ssize_t ret; - - if (len < 1) - { - /* nothing to be done */ - return TLS_EOK; - } - - if ((ret = gnutls_record_send(tls->session, s, len)) < 0) - { - if (ret == GNUTLS_E_INTERRUPTED) - { - *errstr = xasprintf(_("operation aborted")); - } - else if (ret == GNUTLS_E_AGAIN) - { - /* This error message makes more sense than what - * gnutls_strerror() would return. */ - *errstr = xasprintf(_("cannot write to TLS connection: %s"), - _("the operation timed out")); - } - else - { - *errstr = xasprintf(_("cannot write to TLS connection: %s"), - gnutls_strerror((int)ret)); - } - return TLS_EIO; - } - else if ((size_t)ret == len) - { - return TLS_EOK; - } - else /* 0 <= error_code < len */ - { - *errstr = xasprintf(_("cannot write to TLS connection: %s"), - _("unknown error")); - return TLS_EIO; - } - -#endif /* HAVE_LIBGNUTLS */ - -#ifdef HAVE_OPENSSL - - int error_code; - - if (len < 1) - { - /* nothing to be done */ - return TLS_EOK; - } - - if ((error_code = SSL_write(tls->ssl, s, (int)len)) != (int)len) - { - if (errno == EINTR - && ((SSL_get_error(tls->ssl, error_code) == SSL_ERROR_WANT_READ - || SSL_get_error(tls->ssl, error_code) - == SSL_ERROR_WANT_WRITE))) - { - *errstr = xasprintf(_("operation aborted")); - } - else - { - *errstr = openssl_io_error(error_code, - SSL_get_error(tls->ssl, error_code), - _("cannot write to TLS connection")); - } - return TLS_EIO; - } - - return TLS_EOK; - -#endif /* HAVE_OPENSSL */ -} - - -/* - * tls_close() - * - * see tls.h - */ - -void tls_close(tls_t *tls) -{ - if (tls->is_active) - { -#ifdef HAVE_LIBGNUTLS - gnutls_bye(tls->session, GNUTLS_SHUT_WR); - gnutls_deinit(tls->session); - gnutls_certificate_free_credentials(tls->cred); -#endif /* HAVE_LIBGNUTLS */ -#ifdef HAVE_OPENSSL - SSL_shutdown(tls->ssl); - SSL_free(tls->ssl); - SSL_CTX_free(tls->ssl_ctx); -#endif /* HAVE_OPENSSL */ - } - tls_clear(tls); -} - - -/* - * tls_lib_deinit() - * - * see tls.h - */ - -void tls_lib_deinit(void) -{ #ifdef HAVE_LIBGNUTLS gnutls_global_deinit(); +#ifdef NEED_GCRYPT_LOCKING // fixing memory leak in gcryptlib - for (Mutexes::iterator it = g_pGCryptLibMutexes->begin(); it != g_pGCryptLibMutexes->end(); - it++) + for (Mutexes::iterator it = g_pGCryptLibMutexes->begin(); it != g_pGCryptLibMutexes->end(); it++) { delete *it; } delete g_pGCryptLibMutexes; +#endif /* NEED_GCRYPT_LOCKING */ #endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + int iMaxMutexes = CRYPTO_num_locks(); + for (int i=0; i < iMaxMutexes; i++) + { + delete g_pOpenSSLMutexes[i]; + } + free(g_pOpenSSLMutexes); +#endif /* HAVE_OPENSSL */ +} + +TLSSocket::TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile) +{ + m_iSocket = iSocket; + m_bIsClient = bIsClient; + m_szCertFile = szCertFile ? strdup(szCertFile) : NULL; + m_szKeyFile = szKeyFile ? strdup(szKeyFile) : NULL; + m_pContext = NULL; + m_pSession = NULL; + m_bSuppressErrors = false; + m_bInitialized = false; + m_bConnected = false; +} + +TLSSocket::~TLSSocket() +{ + if (m_szCertFile) + { + free(m_szCertFile); + } + if (m_szKeyFile) + { + free(m_szKeyFile); + } + Close(); +} + +void TLSSocket::ReportError(const char* szErrMsg) +{ +#ifdef HAVE_LIBGNUTLS + const char* errstr = gnutls_strerror(m_iRetCode); + if (m_bSuppressErrors) + { + debug("%s: %s", szErrMsg, errstr); + } + else + { + error("%s: %s", szErrMsg, errstr); + } +#endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + int errcode; + do + { + errcode = ERR_get_error(); + + char errstr[1024]; + ERR_error_string_n(errcode, errstr, sizeof(errstr)); + errstr[1024-1] = '\0'; + + if (m_bSuppressErrors) + { + debug("%s: %s", szErrMsg, errstr); + } + else + { + error("%s: %s", szErrMsg, errstr); + } + } while (errcode); +#endif /* HAVE_OPENSSL */ +} + +bool TLSSocket::Start() +{ +#ifdef HAVE_LIBGNUTLS + gnutls_certificate_credentials_t cred; + m_iRetCode = gnutls_certificate_allocate_credentials(&cred); + if (m_iRetCode != 0) + { + ReportError("Could not create TLS context"); + return false; + } + + m_pContext = cred; + + if (m_szCertFile && m_szKeyFile) + { + m_iRetCode = gnutls_certificate_set_x509_key_file((gnutls_certificate_credentials_t)m_pContext, + m_szCertFile, m_szKeyFile, GNUTLS_X509_FMT_PEM); + if (m_iRetCode != 0) + { + ReportError("Could not load certificate or key file"); + Close(); + return false; + } + } + + gnutls_session_t sess; + m_iRetCode = gnutls_init(&sess, m_bIsClient ? GNUTLS_CLIENT : GNUTLS_SERVER); + if (m_iRetCode != 0) + { + ReportError("Could not create TLS session"); + Close(); + return false; + } + + m_pSession = sess; + + m_bInitialized = true; + + m_iRetCode = gnutls_set_default_priority((gnutls_session_t)m_pSession); + if (m_iRetCode != 0) + { + ReportError("Could not initialize TLS session"); + Close(); + return false; + } + + m_iRetCode = gnutls_credentials_set((gnutls_session_t)m_pSession, GNUTLS_CRD_CERTIFICATE, + (gnutls_certificate_credentials_t*)m_pContext); + if (m_iRetCode != 0) + { + ReportError("Could not initialize TLS session"); + Close(); + return false; + } + + gnutls_transport_set_ptr((gnutls_session_t)m_pSession, (gnutls_transport_ptr_t)(size_t)m_iSocket); + + m_iRetCode = gnutls_handshake((gnutls_session_t)m_pSession); + if (m_iRetCode != 0) + { + ReportError("TLS handshake failed"); + Close(); + return false; + } + + m_bConnected = true; + return true; +#endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + m_pContext = SSL_CTX_new(SSLv23_method()); + + if (!m_pContext) + { + ReportError("Could not create TLS context"); + return false; + } + + if (m_szCertFile && m_szKeyFile) + { + if (SSL_CTX_use_certificate_file((SSL_CTX*)m_pContext, m_szCertFile, SSL_FILETYPE_PEM) != 1) + { + ReportError("Could not load certificate file"); + Close(); + return false; + } + if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_pContext, m_szKeyFile, SSL_FILETYPE_PEM) != 1) + { + ReportError("Could not load key file"); + Close(); + return false; + } + } + + m_pSession = SSL_new((SSL_CTX*)m_pContext); + if (!m_pSession) + { + ReportError("Could not create TLS session"); + Close(); + return false; + } + + if (!SSL_set_fd((SSL*)m_pSession, m_iSocket)) + { + ReportError("Could not set the file descriptor for TLS"); + Close(); + return false; + } + + int error_code = m_bIsClient ? SSL_connect((SSL*)m_pSession) : SSL_accept((SSL*)m_pSession); + if (error_code < 1) + { + ReportError("TLS handshake failed"); + Close(); + return false; + } + + m_bConnected = true; + return true; +#endif /* HAVE_OPENSSL */ +} + +void TLSSocket::Close() +{ + if (m_pSession) + { +#ifdef HAVE_LIBGNUTLS + if (m_bConnected) + { + gnutls_bye((gnutls_session_t)m_pSession, GNUTLS_SHUT_WR); + } + if (m_bInitialized) + { + gnutls_deinit((gnutls_session_t)m_pSession); + } +#endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + if (m_bConnected) + { + SSL_shutdown((SSL*)m_pSession); + } + SSL_free((SSL*)m_pSession); +#endif /* HAVE_OPENSSL */ + + m_pSession = NULL; + } + + if (m_pContext) + { +#ifdef HAVE_LIBGNUTLS + gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)m_pContext); +#endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + SSL_CTX_free((SSL_CTX*)m_pContext); +#endif /* HAVE_OPENSSL */ + + m_pContext = NULL; + } +} + +int TLSSocket::Send(const char* pBuffer, int iSize) +{ + int ret; + +#ifdef HAVE_LIBGNUTLS + ret = gnutls_record_send((gnutls_session_t)m_pSession, pBuffer, iSize); +#endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + ret = SSL_write((SSL*)m_pSession, pBuffer, iSize); +#endif /* HAVE_OPENSSL */ + + if (ret < 0) + { + ReportError("Could not read from TLS-Socket"); + return -1; + } + + return ret; +} + +int TLSSocket::Recv(char* pBuffer, int iSize) +{ + int ret; + +#ifdef HAVE_LIBGNUTLS + ret = gnutls_record_recv((gnutls_session_t)m_pSession, pBuffer, iSize); +#endif /* HAVE_LIBGNUTLS */ + +#ifdef HAVE_OPENSSL + ret = SSL_read((SSL*)m_pSession, pBuffer, iSize); +#endif /* HAVE_OPENSSL */ + + if (ret < 0) + { + ReportError("Could not read from TLS-Socket"); + return -1; + } + + return ret; } #endif diff --git a/TLS.h b/TLS.h index 7a3fb911..de658485 100644 --- a/TLS.h +++ b/TLS.h @@ -1,13 +1,7 @@ /* * This file is part of nzbget * - * Based on "tls.h" from project "mpop" by Martin Lambers - * Original source code available on http://sourceforge.net/projects/mpop/ - * - * Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007 - * Martin Lambers - * - * Copyright (C) 2008-2009 Andrey Prygunkov + * Copyright (C) 2008-2013 Andrey Prygunkov * * 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 @@ -33,180 +27,35 @@ #ifndef DISABLE_TLS -#include -#ifdef HAVE_LIBGNUTLS -# include -#endif /* HAVE_LIBGNUTLS */ -#ifdef HAVE_OPENSSL -# include -#endif /* HAVE_OPENSSL */ - - -/* - * If a function with an 'errstr' argument returns a value != TLS_EOK, - * '*errstr' either points to an allocates string containing an error - * description or is NULL. - * If such a function returns TLS_EOK, 'errstr' will not be changed. - */ -#define TLS_EOK 0 /* no error */ -#define TLS_ELIBFAILED 1 /* The underlying library failed */ -#define TLS_ESEED 2 /* Cannot seed pseudo random number generator */ -#define TLS_ECERT 3 /* Certificate check or verification failed */ -#define TLS_EIO 4 /* Input/output error */ -#define TLS_EFILE 5 /* A file does not exist/cannot be read */ -#define TLS_EHANDSHAKE 6 /* TLS handshake failed */ - - -/* - * Always use tls_clear() before using a tls_t! - * Never call a tls_*() function with tls_t NULL! - */ - -typedef struct +class TLSSocket { - int have_trust_file; - int is_active; -#ifdef HAVE_LIBGNUTLS - gnutls_session_t session; - gnutls_certificate_credentials_t cred; -#endif /* HAVE_LIBGNUTLS */ -#ifdef HAVE_OPENSSL - SSL_CTX *ssl_ctx; - SSL *ssl; -#endif /* HAVE_OPENSSL */ -} tls_t; +private: + bool m_bIsClient; + char* m_szCertFile; + char* m_szKeyFile; + SOCKET m_iSocket; + bool m_bSuppressErrors; + int m_iRetCode; + bool m_bInitialized; + bool m_bConnected; -/* - * Information about a X509 certificate. - * The 6 owner_info and issuer_info fields are: - * Common Name - * Organization - * Organizational unit - * Locality - * State/Province - * Country - * Each of these entries may be NULL if it was not provided. - */ -typedef struct -{ - unsigned char sha1_fingerprint[20]; - unsigned char md5_fingerprint[16]; - time_t activation_time; - time_t expiration_time; - char *owner_info[6]; - char *issuer_info[6]; -} tls_cert_info_t; + // using "void*" to prevent the including of GnuTLS/OpenSSL header files into TLS.h + void* m_pContext; + void* m_pSession; -/* - * tls_lib_init() - * - * Initialize underlying TLS library. If this function returns TLS_ELIBFAILED, - * *errstr will always point to an error string. - * Used error codes: TLS_ELIBFAILED - */ -int tls_lib_init(char **errstr); + void ReportError(const char* szErrMsg); -/* - * tls_clear() - * - * Clears a tls_t type (marks it inactive). - */ -void tls_clear(tls_t *tls); - -/* - * tls_init() - * - * Initializes a tls_t. If both 'key_file' and 'ca_file' are not NULL, they are - * set to be used when the peer request a certificate. If 'trust_file' is not - * NULL, it will be used to verify the peer certificate. - * All files must be in PEM format. - * If 'force_sslv3' is set, then only the SSLv3 protocol will be accepted. This - * option might be needed to talk to some obsolete broken servers. Only use this - * if you have to. - * Used error codes: TLS_ELIBFAILED, TLS_EFILE - */ -int tls_init(tls_t *tls, - const char *key_file, const char *ca_file, const char *trust_file, - int force_sslv3, char **errstr); - -/* - * tls_start() - * - * Starts TLS encryption on a socket. - * 'tls' must be initialized using tls_init(). - * If 'no_certcheck' is true, then no checks will be performed on the peer - * certificate. If it is false and no trust file was set with tls_init(), - * only sanity checks are performed on the peer certificate. If it is false - * and a trust file was set, real verification of the peer certificate is - * performed. - * 'hostname' is the host to start TLS with. It is needed for sanity checks/ - * verification. - * 'tci' must be allocated with tls_cert_info_new(). Information about the - * peer's certificata will be stored in it. It can later be freed with - * tls_cert_info_free(). 'tci' is allowed to be NULL; no certificate - * information will be passed in this case. - * Used error codes: TLS_ELIBFAILED, TLS_ECERT, TLS_EHANDSHAKE - */ -int tls_start(tls_t *tls, int fd, const char *hostname, int no_certcheck, - tls_cert_info_t *tci, char **errstr); - -/* - * tls_is_active() - * - * Returns whether 'tls' is an active TLS connection. - */ -int tls_is_active(tls_t *tls); - -/* - * tls_cert_info_new() - * Returns a new tls_cert_info_t - */ -tls_cert_info_t *tls_cert_info_new(void); - -/* - * tls_cert_info_free() - * Frees a tls_cert_info_t - */ -void tls_cert_info_free(tls_cert_info_t *tci); - -/* - * tls_cert_info_get() - * - * Extracts certificate information from the TLS connection 'tls' and stores - * it in 'tci'. See the description of tls_cert_info_t above. - * Used error codes: TLS_ECERT - */ -int tls_cert_info_get(tls_t *tls, tls_cert_info_t *tci, char **errstr); - -/* - * tls_getbuf() - * - * Reads a buffer using TLS and stores it in 's'. - * Used error codes: TLS_EIO - */ -int tls_getbuf(tls_t *tls, char* s, size_t len, size_t* readlen, char **errstr); - -/* - * tls_putbuf() - * - * Writes 'len' characters from the string 's' using TLS. - * Used error codes: TLS_EIO - */ -int tls_putbuf(tls_t *tls, const char *s, size_t len, char **errstr); - -/* - * tls_close() - * - * Close a TLS connection and mark it inactive - */ -void tls_close(tls_t *tls); - -/* - * tls_lib_deinit() - * - * Deinit underlying TLS library. - */ -void tls_lib_deinit(void); +public: + TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile); + ~TLSSocket(); + static void Init(); + static void Final(); + bool Start(); + void Close(); + int Send(const char* pBuffer, int iSize); + int Recv(char* pBuffer, int iSize); + void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; } +}; #endif #endif diff --git a/nzbget.conf b/nzbget.conf index 76c7544f..9449f475 100644 --- a/nzbget.conf +++ b/nzbget.conf @@ -157,6 +157,9 @@ Server1.Connections=4 ControlIP=0.0.0.0 # Port which NZBGet server and remote client use (1-65535). +# +# NOTE: The communication via this port is not encrypted. For encrypted +# communication see option . ControlPort=6789 # Password which NZBGet server and remote client use. @@ -165,7 +168,22 @@ ControlPort=6789 # and the password defined here. ControlPassword=tegbzn6789 -# See also option in section "LOGGING" +# Secure control of NZBGet server (yes, no). +# +# Activate the option if you want to access NZBGet built-in web-server +# via HTTPS (web-interface and RPC). You should also provide certificate +# and key files, see option and option . +SecureControl=no + +# Port which NZBGet server and remote client use for encrypted +# communication (1-65535). +SecurePort=6791 + +# Full path to certificate file for encrypted communication. +SecureCert= + +# Full path to key file for encrypted communication. +SecureKey= ############################################################################## @@ -750,8 +768,8 @@ ParRepair=yes # # NOTE: for par-check/repair NZBGet uses library libpar2. The last and # widely used version 0.2 of the library has few bugs, sometimes causing -# a crash of the program. This is especially true when using "full" or -# "auto" par-scan. NZBGet is supplied with patches addressing these +# a crash of the program. This is expecially true when using "full" or +# "auto" par-scan. NZBGet is supplied with patches addressing this # issues. Please apply the patches to libpar2 and recompile it. ParScan=limited diff --git a/nzbget.cpp b/nzbget.cpp index 5fac31ce..3202814f 100644 --- a/nzbget.cpp +++ b/nzbget.cpp @@ -106,6 +106,7 @@ ServerPool* g_pServerPool = NULL; QueueCoordinator* g_pQueueCoordinator = NULL; UrlCoordinator* g_pUrlCoordinator = NULL; RemoteServer* g_pRemoteServer = NULL; +RemoteServer* g_pRemoteSecureServer = NULL; DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL; DownloadQueueHolder* g_pDownloadQueueHolder = NULL; Log* g_pLog = NULL; @@ -293,8 +294,14 @@ void Run(bool bReload) // Setup the network-server if (g_pOptions->GetServerMode()) { - g_pRemoteServer = new RemoteServer(); + g_pRemoteServer = new RemoteServer(false); g_pRemoteServer->Start(); + + if (g_pOptions->GetSecureControl()) + { + g_pRemoteSecureServer = new RemoteServer(true); + g_pRemoteSecureServer->Start(); + } } // Creating PrePostProcessor @@ -405,6 +412,24 @@ void Run(bool bReload) debug("RemoteServer stopped"); } + if (g_pRemoteSecureServer) + { + debug("stopping RemoteSecureServer"); + g_pRemoteSecureServer->Stop(); + int iMaxWaitMSec = 1000; + while (g_pRemoteSecureServer->IsRunning() && iMaxWaitMSec > 0) + { + usleep(100 * 1000); + iMaxWaitMSec -= 100; + } + if (g_pRemoteSecureServer->IsRunning()) + { + debug("Killing RemoteSecureServer"); + g_pRemoteSecureServer->Kill(); + } + debug("RemoteSecureServer stopped"); + } + // Stop Frontend if (g_pFrontend) { @@ -703,6 +728,14 @@ void Cleanup() } debug("RemoteServer deleted"); + debug("Deleting RemoteSecureServer"); + if (g_pRemoteSecureServer) + { + delete g_pRemoteSecureServer; + g_pRemoteSecureServer = NULL; + } + debug("RemoteSecureServer deleted"); + debug("Deleting PrePostProcessor"); if (g_pPrePostProcessor) { diff --git a/nzbget.h b/nzbget.h index f288c0a9..ddf79fe9 100644 --- a/nzbget.h +++ b/nzbget.h @@ -50,6 +50,7 @@ #define usleep(usec) Sleep((usec) / 1000) #define gettimeofday(tm, ignore) _ftime(tm) #define socklen_t int +#define SHUT_WR 0x01 #define SHUT_RDWR 0x02 #define PATH_SEPARATOR '\\' #define ALT_PATH_SEPARATOR '/' diff --git a/webui/index.html b/webui/index.html index 1c7d95c4..983f7822 100644 --- a/webui/index.html +++ b/webui/index.html @@ -550,8 +550,6 @@

The original project was initially created by Sven Henkel (sidddy@users.sourceforge.net) in 2004 and later developed by Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005. In 2007 the abandoned project was overtaken by Andrey Prygunkov. Since then the program has been completely rewritten.

-

Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers (marlam@marlam.de).

-

Copyright

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 diff --git a/win32.h b/win32.h index 98590367..cc94bb09 100644 --- a/win32.h +++ b/win32.h @@ -1,7 +1,7 @@ /* * This file is part of nzbget * - * Copyright (C) 2007-2010 Andrey Prygunkov + * Copyright (C) 2007-2013 Andrey Prygunkov * * 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 @@ -36,6 +36,8 @@ #ifndef DISABLE_TLS /* Define to 1 to use GnuTLS library for TLS/SSL-support */ #define HAVE_LIBGNUTLS +/* Define to 1 to use OpenSSL library for TLS/SSL-support */ +//#define HAVE_OPENSSL #endif /* Define to the name of macro which returns the name of function being