]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_ssl_openssl.cpp
Merge pull request #1106 from SaberUK/master+purge-pkg-path
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_openssl.cpp
index 684ee218060dda2f28effb766d7bf17dd3b30d83..c9ae14e11bad0b31547494d3b19cd9d3a052d199 100644 (file)
@@ -126,7 +126,12 @@ namespace OpenSSL
 #endif
 
                        ctx_options = SSL_CTX_set_options(ctx, opts);
-                       SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+                       long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
+#ifdef SSL_MODE_RELEASE_BUFFERS
+                       mode |= SSL_MODE_RELEASE_BUFFERS;
+#endif
+                       SSL_CTX_set_mode(ctx, mode);
                        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
                        SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
                        SSL_CTX_set_info_callback(ctx, StaticSSLInfoCallback);
@@ -264,10 +269,10 @@ namespace OpenSSL
                        long setoptions = tag->getInt(ctxname + "setoptions");
                        long clearoptions = tag->getInt(ctxname + "clearoptions");
 #ifdef SSL_OP_NO_COMPRESSION
-                       if (!tag->getBool("compression", true))
+                       if (!tag->getBool("compression", false)) // Disable compression by default
                                setoptions |= SSL_OP_NO_COMPRESSION;
 #endif
-                       if (!tag->getBool("sslv3", true))
+                       if (!tag->getBool("sslv3", false)) // Disable SSLv3 by default
                                setoptions |= SSL_OP_NO_SSLv3;
                        if (!tag->getBool("tlsv1", true))
                                setoptions |= SSL_OP_NO_TLSv1;
@@ -349,8 +354,49 @@ namespace OpenSSL
                bool AllowRenegotiation() const { return allowrenego; }
                unsigned int GetOutgoingRecordSize() const { return outrecsize; }
        };
+
+       namespace BIOMethod
+       {
+               static int create(BIO* bio)
+               {
+                       bio->init = 1;
+                       return 1;
+               }
+
+               static int destroy(BIO* bio)
+               {
+                       // XXX: Dummy function to avoid a memory leak in OpenSSL.
+                       // The memory leak happens in BIO_free() (bio_lib.c) when the destroy func of the BIO is NULL.
+                       // This is fixed in OpenSSL but some distros still ship the unpatched version hence we provide this workaround.
+                       return 1;
+               }
+
+               static long ctrl(BIO* bio, int cmd, long num, void* ptr)
+               {
+                       if (cmd == BIO_CTRL_FLUSH)
+                               return 1;
+                       return 0;
+               }
+
+               static int read(BIO* bio, char* buf, int len);
+               static int write(BIO* bio, const char* buf, int len);
+       }
 }
 
+static BIO_METHOD biomethods =
+{
+       (100 | BIO_TYPE_SOURCE_SINK),
+       "inspircd",
+       OpenSSL::BIOMethod::write,
+       OpenSSL::BIOMethod::read,
+       NULL, // puts
+       NULL, // gets
+       OpenSSL::BIOMethod::ctrl,
+       OpenSSL::BIOMethod::create,
+       OpenSSL::BIOMethod::destroy, // destroy, does nothing, see function body for more info
+       NULL // callback_ctrl
+};
+
 static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
 {
        /* XXX: This will allow self signed certificates.
@@ -498,7 +544,9 @@ class OpenSSLIOHook : public SSLIOHook
                        // The other side is trying to renegotiate, kill the connection and change status
                        // to ISSL_NONE so CheckRenego() closes the session
                        status = ISSL_NONE;
-                       SocketEngine::Shutdown(SSL_get_fd(sess), 2);
+                       BIO* bio = SSL_get_rbio(sess);
+                       EventHandler* eh = static_cast<StreamSocket*>(bio->ptr);
+                       SocketEngine::Shutdown(eh, 2);
                }
        }
 
@@ -539,10 +587,10 @@ class OpenSSLIOHook : public SSLIOHook
                , data_to_write(false)
                , profile(sslprofile)
        {
-               if (sess == NULL)
-                       return;
-               if (SSL_set_fd(sess, sock->GetFd()) == 0)
-                       throw ModuleException("Can't set fd with SSL_set_fd: " + ConvToStr(sock->GetFd()));
+               // Create BIO instance and store a pointer to the socket in it which will be used by the read and write functions
+               BIO* bio = BIO_new(&biomethods);
+               bio->ptr = sock;
+               SSL_set_bio(sess, bio, bio);
 
                SSL_set_ex_data(sess, exdataindex, this);
                sock->AddIOHook(this);
@@ -702,6 +750,52 @@ static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
        hook->SSLInfoCallback(where, rc);
 }
 
+static int OpenSSL::BIOMethod::write(BIO* bio, const char* buffer, int size)
+{
+       BIO_clear_retry_flags(bio);
+
+       StreamSocket* sock = static_cast<StreamSocket*>(bio->ptr);
+       if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
+       {
+               // Writes blocked earlier, don't retry syscall
+               BIO_set_retry_write(bio);
+               return -1;
+       }
+
+       int ret = SocketEngine::Send(sock, buffer, size, 0);
+       if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+       {
+               // Blocked, set retry flag for OpenSSL
+               SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
+               BIO_set_retry_write(bio);
+       }
+
+       return ret;
+}
+
+static int OpenSSL::BIOMethod::read(BIO* bio, char* buffer, int size)
+{
+       BIO_clear_retry_flags(bio);
+
+       StreamSocket* sock = static_cast<StreamSocket*>(bio->ptr);
+       if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
+       {
+               // Reads blocked earlier, don't retry syscall
+               BIO_set_retry_read(bio);
+               return -1;
+       }
+
+       int ret = SocketEngine::Recv(sock, buffer, size, 0);
+       if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
+       {
+               // Blocked, set retry flag for OpenSSL
+               SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
+               BIO_set_retry_read(bio);
+       }
+
+       return ret;
+}
+
 class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider
 {
        reference<OpenSSL::Profile> profile;