]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_ssl_openssl.cpp
m_ssl_openssl Verify DH params being non-NULL before setting it on the context
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_openssl.cpp
index e9b5c4052e1858e0472add603aa70138880bd14f..aee7a5e3428a86afc8e2817c21fea3566aed3365 100644 (file)
 /* $NoPedantic */
 
 
+class ModuleSSLOpenSSL;
+
 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
 
 static bool SelfSigned = false;
 
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+static ModuleSSLOpenSSL* opensslmod = NULL;
+#endif
+
 char* get_error()
 {
        return ERR_error_string(ERR_get_error(), NULL);
@@ -75,6 +81,8 @@ public:
        bool data_to_write;
 
        issl_session()
+               : sess(NULL)
+               , status(ISSL_NONE)
        {
                outbound = false;
                data_to_write = false;
@@ -128,7 +136,7 @@ class ModuleSSLOpenSSL : public Module
                        setoptions |= SSL_OP_NO_TLSv1;
 
                long clearoptions = tag->getInt(ctxname + "clearoptions");
-               ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Setting OpenSSL %s context options, default: %ld set: %ld clear: %ld", ctxname.c_str(), defoptions, clearoptions, setoptions);
+               ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Setting OpenSSL %s context options, default: %ld set: %ld clear: %ld", ctxname.c_str(), defoptions, setoptions, clearoptions);
 
                // Clear everything
                SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
@@ -139,10 +147,72 @@ class ModuleSSLOpenSSL : public Module
                ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "OpenSSL %s context options: %ld", ctxname.c_str(), final);
        }
 
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+       void SetupECDH(ConfigTag* tag)
+       {
+               std::string curvename = tag->getString("ecdhcurve", "prime256v1");
+               if (curvename.empty())
+                       return;
+
+               int nid = OBJ_sn2nid(curvename.c_str());
+               if (nid == 0)
+               {
+                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unknown curve: \"%s\"", curvename.c_str());
+                       return;
+               }
+
+               EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
+               if (!eckey)
+               {
+                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Unable to create EC key object");
+                       return;
+               }
+
+               ERR_clear_error();
+               if (SSL_CTX_set_tmp_ecdh(ctx, eckey) < 0)
+               {
+                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set ECDH parameters");
+                       ERR_print_errors_cb(error_callback, this);
+               }
+
+               EC_KEY_free(eckey);
+       }
+#endif
+
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+       static void SSLInfoCallback(const SSL* ssl, int where, int rc)
+       {
+               int fd = SSL_get_fd(const_cast<SSL*>(ssl));
+               issl_session& session = opensslmod->sessions[fd];
+
+               if ((where & SSL_CB_HANDSHAKE_START) && (session.status == ISSL_OPEN))
+               {
+                       // The other side is trying to renegotiate, kill the connection and change status
+                       // to ISSL_NONE so CheckRenego() closes the session
+                       session.status = ISSL_NONE;
+                       ServerInstance->SE->Shutdown(fd, 2);
+               }
+       }
+
+       bool CheckRenego(StreamSocket* sock, issl_session* session)
+       {
+               if (session->status != ISSL_NONE)
+                       return true;
+
+               ServerInstance->Logs->Log("m_ssl_openssl", DEBUG, "Session %p killed, attempted to renegotiate", (void*)session->sess);
+               CloseSession(session);
+               sock->SetError("Renegotiation is not allowed");
+               return false;
+       }
+#endif
+
  public:
 
        ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
        {
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+               opensslmod = this;
+#endif
                sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
 
                /* Global SSL library initialization*/
@@ -201,6 +271,20 @@ class ModuleSSLOpenSSL : public Module
 
                ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
 
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+               // Set the callback if we are not allowing renegotiations, unset it if we do
+               if (Conf->getBool("renegotiation", true))
+               {
+                       SSL_CTX_set_info_callback(ctx, NULL);
+                       SSL_CTX_set_info_callback(clictx, NULL);
+               }
+               else
+               {
+                       SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
+                       SSL_CTX_set_info_callback(clictx, SSLInfoCallback);
+               }
+#endif
+
                if (Conf->getBool("showports", true))
                {
                        sslports = Conf->getString("advertisedports");
@@ -321,17 +405,28 @@ class ModuleSSLOpenSSL : public Module
 #endif
 
                        ERR_clear_error();
-                       if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
+                       if (ret)
                        {
-                               ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
-                               ERR_print_errors_cb(error_callback, this);
+                               if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
+                               {
+                                       ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
+                                       ERR_print_errors_cb(error_callback, this);
+                               }
+                               DH_free(ret);
+                       }
+                       else
+                       {
+                               ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s.", dhfile.c_str());
                        }
-                       DH_free(ret);
                }
 
 #ifndef _WIN32
                fclose(dhpfile);
 #endif
+
+#ifdef INSPIRCD_OPENSSL_ENABLE_ECDH
+               SetupECDH(conf);
+#endif
        }
 
        void On005Numeric(std::string &output)
@@ -392,6 +487,12 @@ class ModuleSSLOpenSSL : public Module
 
                        req.cert = session->cert;
                }
+               else if (!strcmp("GET_RAW_SSL_SESSION", request.id))
+               {
+                       SSLRawSessionRequest& req = static_cast<SSLRawSessionRequest&>(request);
+                       if ((req.fd >= 0) && (req.fd < ServerInstance->SE->GetMaxFds()))
+                               req.data = reinterpret_cast<void*>(sessions[req.fd].sess);
+               }
        }
 
        void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
@@ -403,7 +504,7 @@ class ModuleSSLOpenSSL : public Module
                session->sess = SSL_new(ctx);
                session->status = ISSL_NONE;
                session->outbound = false;
-               session->cert = NULL;
+               session->data_to_write = false;
 
                if (session->sess == NULL)
                        return;
@@ -429,6 +530,7 @@ class ModuleSSLOpenSSL : public Module
                session->sess = SSL_new(clictx);
                session->status = ISSL_NONE;
                session->outbound = true;
+               session->data_to_write = false;
 
                if (session->sess == NULL)
                        return;
@@ -488,11 +590,23 @@ class ModuleSSLOpenSSL : public Module
                        size_t bufsiz = ServerInstance->Config->NetBufferSize;
                        int ret = SSL_read(session->sess, buffer, bufsiz);
 
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+                       if (!CheckRenego(user, session))
+                               return -1;
+#endif
+
                        if (ret > 0)
                        {
                                recvq.append(buffer, ret);
+
+                               int mask = 0;
+                               // Schedule a read if there is still data in the OpenSSL buffer
+                               if (SSL_pending(session->sess) > 0)
+                                       mask |= FD_ADD_TRIAL_READ;
                                if (session->data_to_write)
-                                       ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
+                                       mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
+                               if (mask != 0)
+                                       ServerInstance->SE->ChangeEventMask(user, mask);
                                return 1;
                        }
                        else if (ret == 0)
@@ -556,6 +670,12 @@ class ModuleSSLOpenSSL : public Module
                {
                        ERR_clear_error();
                        int ret = SSL_write(session->sess, buffer.data(), buffer.size());
+
+#ifdef INSPIRCD_OPENSSL_ENABLE_RENEGO_DETECTION
+                       if (!CheckRenego(user, session))
+                               return -1;
+#endif
+
                        if (ret == (int)buffer.length())
                        {
                                session->data_to_write = false;
@@ -644,10 +764,8 @@ class ModuleSSLOpenSSL : public Module
                else if (ret == 0)
                {
                        CloseSession(session);
-                       return false;
                }
-
-               return true;
+               return false;
        }
 
        void CloseSession(issl_session* session)
@@ -660,7 +778,7 @@ class ModuleSSLOpenSSL : public Module
 
                session->sess = NULL;
                session->status = ISSL_NONE;
-               errno = EIO;
+               session->cert = NULL;
        }
 
        void VerifyCertificate(issl_session* session, StreamSocket* user)