diff --git a/NetSSL_OpenSSL/include/Poco/Net/Context.h b/NetSSL_OpenSSL/include/Poco/Net/Context.h index 3e3131d2f..d99639564 100644 --- a/NetSSL_OpenSSL/include/Poco/Net/Context.h +++ b/NetSSL_OpenSSL/include/Poco/Net/Context.h @@ -126,6 +126,16 @@ public: PROTO_TLSV1_3 = 0x20 }; + enum SecurityLevel + { + SECURITY_LEVEL_NONE = 0, + SECURITY_LEVEL_80_BITS = 1, + SECURITY_LEVEL_112_BITS = 2, + SECURITY_LEVEL_128_BITS = 3, + SECURITY_LEVEL_192_BITS = 4, + SECURITY_LEVEL_256_BITS = 5 + }; + struct NetSSL_API Params { Params(); @@ -193,6 +203,11 @@ public: /// and other TLSv1.3 ephemeral key negotiation, based /// on the group names defined by OpenSSL. Defaults to /// "X448:X25519:ffdhe4096:ffdhe3072:ffdhe2048:ffdhe6144:ffdhe8192:P-521:P-384:P-256" + + SecurityLevel securityLevel; + /// Defines minimal number of security bits allowed. + /// Requires OpenSSL >= 1.1 to be effective. + }; using InvalidCertificateHandlerPtr = Poco::SharedPtr; @@ -420,6 +435,9 @@ public: /// Returns the InvalidCertificateHandler set for this Context, /// or a null pointer if none has been set. + void setSecurityLevel(SecurityLevel level); + /// Sets the security level. + private: void init(const Params& params); /// Initializes the Context with the given parameters. diff --git a/NetSSL_OpenSSL/include/Poco/Net/FTPSClientSession.h b/NetSSL_OpenSSL/include/Poco/Net/FTPSClientSession.h index 2ef409bfc..b9466c884 100644 --- a/NetSSL_OpenSSL/include/Poco/Net/FTPSClientSession.h +++ b/NetSSL_OpenSSL/include/Poco/Net/FTPSClientSession.h @@ -64,6 +64,9 @@ public: bool isSecure() const; /// Returns true if the session is FTPS. + void forceSessionReuse(bool force = true); + /// Enable or disable session reusing + protected: virtual StreamSocket establishDataConnection(const std::string& command, const std::string& arg); /// Create secure data connection @@ -80,6 +83,7 @@ private: bool _enableFTPS = true; bool _secureDataConnection = false; + bool _forceSessionReuse = false; Context::Ptr _pContext; }; @@ -95,6 +99,12 @@ inline bool FTPSClientSession::isSecure() const } +inline void FTPSClientSession::forceSessionReuse(bool force) +{ + _forceSessionReuse = force; +} + + } } // namespace Poco::Net diff --git a/NetSSL_OpenSSL/include/Poco/Net/SecureSocketImpl.h b/NetSSL_OpenSSL/include/Poco/Net/SecureSocketImpl.h index 5940ae598..7bc7514b8 100644 --- a/NetSSL_OpenSSL/include/Poco/Net/SecureSocketImpl.h +++ b/NetSSL_OpenSSL/include/Poco/Net/SecureSocketImpl.h @@ -281,6 +281,7 @@ private: bool _needHandshake; std::string _peerHostName; Session::Ptr _pSession; + bool _bidirectShutdown = true; friend class SecureStreamSocketImpl; }; diff --git a/NetSSL_OpenSSL/src/Context.cpp b/NetSSL_OpenSSL/src/Context.cpp index f5d15e334..0fa6e349b 100644 --- a/NetSSL_OpenSSL/src/Context.cpp +++ b/NetSSL_OpenSSL/src/Context.cpp @@ -36,7 +36,8 @@ Context::Params::Params(): loadDefaultCAs(false), ocspStaplingVerification(false), cipherList("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"), - dhUse2048Bits(false) + dhUse2048Bits(false), + securityLevel(SECURITY_LEVEL_NONE) { } @@ -125,6 +126,9 @@ void Context::init(const Params& params) try { int errCode = 0; + + setSecurityLevel(params.securityLevel); + if (!params.caLocation.empty()) { Poco::File aFile(params.caLocation); @@ -204,6 +208,14 @@ void Context::init(const Params& params) } +void Context::setSecurityLevel(SecurityLevel level) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + SSL_CTX_set_security_level(_pSSLContext, static_cast(level)); +#endif +} + + void Context::useCertificate(const Poco::Crypto::X509Certificate& certificate) { int errCode = SSL_CTX_use_certificate(_pSSLContext, const_cast(certificate.certificate())); diff --git a/NetSSL_OpenSSL/src/FTPSClientSession.cpp b/NetSSL_OpenSSL/src/FTPSClientSession.cpp index 0523decee..059c94ebd 100644 --- a/NetSSL_OpenSSL/src/FTPSClientSession.cpp +++ b/NetSSL_OpenSSL/src/FTPSClientSession.cpp @@ -127,6 +127,14 @@ StreamSocket FTPSClientSession::establishDataConnection(const std::string& comma { Poco::Net::SecureStreamSocket sss(Poco::Net::SecureStreamSocket::attach(ss, pSecure->context(), pSecure->currentSession())); ss = sss; + if (_forceSessionReuse) + { + sss.setLazyHandshake(false); + if (sss.completeHandshake() != 1) + throw Poco::Net::FTPException("SSL Session HANDSHAKE error"); + if (!sss.sessionWasReused()) + throw Poco::Net::FTPException("SSL Session for data connection was not reused"); + } } } return ss; diff --git a/NetSSL_OpenSSL/src/SecureSocketImpl.cpp b/NetSSL_OpenSSL/src/SecureSocketImpl.cpp index 9cf81c568..6fd0147d6 100644 --- a/NetSSL_OpenSSL/src/SecureSocketImpl.cpp +++ b/NetSSL_OpenSSL/src/SecureSocketImpl.cpp @@ -255,7 +255,41 @@ void SecureSocketImpl::shutdown() // most web browsers, so we just set the shutdown // flag by calling SSL_shutdown() once and be // done with it. +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + int rc = 0; + if (!_bidirectShutdown) + rc = SSL_shutdown(_pSSL); + else + { + Poco::Timespan recvTimeout = _pSocket->getReceiveTimeout(); + Poco::Timespan pollTimeout(0, 100000); + Poco::Timestamp tsNow; + do + { + rc = SSL_shutdown(_pSSL); + if (rc == 1) break; + if (rc < 0) + { + int err = SSL_get_error(_pSSL, rc); + if (err == SSL_ERROR_WANT_READ) + _pSocket->poll(pollTimeout, Poco::Net::Socket::SELECT_READ); + else if (err == SSL_ERROR_WANT_WRITE) + _pSocket->poll(pollTimeout, Poco::Net::Socket::SELECT_WRITE); + else + { + int socketError = SocketImpl::lastError(); + long lastError = ERR_get_error(); + if ((err == SSL_ERROR_SSL) && (socketError == 0) && (lastError == 0x0A000123)) + rc = 0; + break; + } + } + else _pSocket->poll(pollTimeout, Poco::Net::Socket::SELECT_READ); + } while (!tsNow.isElapsed(recvTimeout.totalMicroseconds())); + } +#else int rc = SSL_shutdown(_pSSL); +#endif if (rc < 0) handleError(rc); if (_pSocket->getBlocking()) { @@ -328,6 +362,7 @@ int SecureSocketImpl::receiveBytes(void* buffer, int length, int flags) rc = SSL_read(_pSSL, buffer, length); } while (mustRetry(rc)); + _bidirectShutdown = false; if (rc <= 0) { return handleError(rc);