]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_ssl_gnutls.cpp
Properly check if the SSL handshake is completed in m_ssl_gnutls, fixes the recent...
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_gnutls.cpp
index 91f29189e0c788e49254295d7db6f7984a76d0b0..151d10fd401543d1411fd56bc548f74391c422b7 100644 (file)
@@ -2,7 +2,7 @@
  *       | Inspire Internet Relay Chat Daemon |
  *       +------------------------------------+
  *
- *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
  * See: http://www.inspircd.org/wiki/index.php/Credits
  *
  * This program is free but copyrighted software; see
  */
 
 #include "inspircd.h"
-
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
-
-#include "inspircd_config.h"
-#include "configreader.h"
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-#include "socket.h"
-#include "hashcomp.h"
 #include "transport.h"
 #include "m_cap.h"
 
@@ -55,6 +46,11 @@ bool isin(const std::string &host, int port, const std::vector<std::string> &por
 class issl_session : public classbase
 {
 public:
+       issl_session()
+       {
+               sess = NULL;
+       }
+
        gnutls_session_t sess;
        issl_status status;
        std::string outbuf;
@@ -75,14 +71,24 @@ class CommandStartTLS : public Command
 
        CmdResult Handle (const std::vector<std::string> &parameters, User *user)
        {
-               if (user->registered == REG_ALL)
+               /* changed from == REG_ALL to catch clients sending STARTTLS
+                * after NICK and USER but before OnUserConnect completes and
+                * give a proper error message (see bug #645) - dz
+                */
+               if (user->registered != REG_NONE)
                {
-                       ServerInstance->Users->QuitUser(user, "STARTTLS not allowed after client registration");
+                       ServerInstance->Users->QuitUser(user, "STARTTLS is not permitted after client registration has started");
                }
                else
                {
-                       user->io = Caller;
-                       Caller->OnRawSocketAccept(user->GetFd(), user->GetIPString(), user->GetPort());
+                       if (!user->GetIOHook())
+                       {
+                               user->WriteNumeric(670, "%s :STARTTLS successful, go ahead with TLS handshake", user->nick.c_str());
+                               user->AddIOHook(Caller);
+                               Caller->OnRawSocketAccept(user->GetFd(), user->GetIPString(), user->GetPort());
+                       }
+                       else
+                               user->WriteNumeric(691, "%s :STARTTLS failure", user->nick.c_str());
                }
 
                return CMD_FAILURE;
@@ -112,6 +118,7 @@ class ModuleSSLGnuTLS : public Module
        int dh_bits;
 
        int clientactive;
+       bool cred_alloc;
 
        CommandStartTLS* starttls;
 
@@ -129,13 +136,7 @@ class ModuleSSLGnuTLS : public Module
 
                gnutls_global_init(); // This must be called once in the program
 
-               if(gnutls_certificate_allocate_credentials(&x509_cred) != 0)
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials");
-
-               // Guessing return meaning
-               if(gnutls_dh_params_init(&dh_params) < 0)
-                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters");
-
+               cred_alloc = false;
                // Needs the flag as it ignores a plain /rehash
                OnRehash(NULL,"ssl");
 
@@ -240,7 +241,22 @@ class ModuleSSLGnuTLS : public Module
                        keyfile = confdir + keyfile;
 
                int ret;
-
+               
+               if (cred_alloc)
+               {
+                       // Deallocate the old credentials
+                       gnutls_dh_params_deinit(dh_params);
+                       gnutls_certificate_free_credentials(x509_cred);
+               }
+               else
+                       cred_alloc = true;
+               
+               if((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
+                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials: %s", gnutls_strerror(ret));
+               
+               if((ret = gnutls_dh_params_init(&dh_params)) < 0)
+                       ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters: %s", gnutls_strerror(ret));
+               
                if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
                        ServerInstance->Logs->Log("m_ssl_gnutls",DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));
 
@@ -250,7 +266,7 @@ class ModuleSSLGnuTLS : public Module
                if((ret = gnutls_certificate_set_x509_key_file (x509_cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)
                {
                        // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException
-                       throw ModuleException("Unable to load GnuTLS server certificate: " + std::string(gnutls_strerror(ret)));
+                       throw ModuleException("Unable to load GnuTLS server certificate (" + certfile + ", key: " + keyfile + "): " + std::string(gnutls_strerror(ret)));
                }
 
                // This may be on a large (once a day or week) timer eventually.
@@ -287,11 +303,12 @@ class ModuleSSLGnuTLS : public Module
                {
                        User* user = (User*)item;
 
-                       if(user->io)
+                       if (user->GetIOHook() == this)
                        {
                                // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
                                // Potentially there could be multiple SSL modules loaded at once on different ports.
                                ServerInstance->Users->QuitUser(user, "SSL module unloading");
+                               user->DelIOHook();
                        }
                        if (user->GetExt("ssl_cert", dummy))
                        {
@@ -300,8 +317,6 @@ class ModuleSSLGnuTLS : public Module
                                delete tofree;
                                user->Shrink("ssl_cert");
                        }
-
-                       user->io = NULL;
                }
        }
 
@@ -320,7 +335,7 @@ class ModuleSSLGnuTLS : public Module
 
        virtual Version GetVersion()
        {
-               return Version(1, 2, 0, 0, VF_VENDOR, API_VERSION);
+               return Version("$Id$", VF_VENDOR, API_VERSION);
        }
 
 
@@ -331,10 +346,10 @@ class ModuleSSLGnuTLS : public Module
 
        virtual void OnHookUserIO(User* user, const std::string &targetip)
        {
-               if (!user->io && isin(targetip,user->GetPort(),listenports))
+               if (!user->GetIOHook() && isin(targetip,user->GetPort(),listenports))
                {
                        /* Hook the user with our module */
-                       user->io = this;
+                       user->AddIOHook(this);
                }
        }
 
@@ -350,7 +365,7 @@ class ModuleSSLGnuTLS : public Module
                        const char* ret = "OK";
                        try
                        {
-                               ret = ServerInstance->Config->AddIOHook((Module*)this, (BufferedSocket*)ISR->Sock) ? "OK" : NULL;
+                               ret = ISR->Sock->AddIOHook((Module*)this) ? "OK" : NULL;
                        }
                        catch (ModuleException &e)
                        {
@@ -360,7 +375,7 @@ class ModuleSSLGnuTLS : public Module
                }
                else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
                {
-                       return ServerInstance->Config->DelIOHook((BufferedSocket*)ISR->Sock) ? "OK" : NULL;
+                       return ISR->Sock->DelIOHook() ? "OK" : NULL;
                }
                else if (strcmp("IS_HSDONE", request->GetId()) == 0)
                {
@@ -523,6 +538,9 @@ class ModuleSSLGnuTLS : public Module
                                }
                                else
                                {
+                                       ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT,
+                                                       "m_ssl_gnutls.so: Error while reading on fd %d: %s",
+                                                       session->fd, gnutls_strerror(ret));
                                        readresult = 0;
                                        CloseSession(session);
                                }
@@ -539,7 +557,7 @@ class ModuleSSLGnuTLS : public Module
                                {
                                        memcpy(buffer, session->inbuf, count);
                                        // Move the stuff left in inbuf to the beginning of it
-                                       memcpy(session->inbuf, session->inbuf + count, (length - count));
+                                       memmove(session->inbuf, session->inbuf + count, (length - count));
                                        // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
                                        session->inbufoffset = length - count;
                                        // Insp uses readresult as the count of how much data there is in buffer, so:
@@ -581,7 +599,7 @@ class ModuleSSLGnuTLS : public Module
                sendbuffer = session->outbuf.c_str();
                count = session->outbuf.size();
 
-               if (session->status == ISSL_HANDSHAKING_WRITE)
+               if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ)
                {
                        // The handshake isn't finished, try to finish it.
                        Handshake(session);
@@ -603,6 +621,9 @@ class ModuleSSLGnuTLS : public Module
                        {
                                if(ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED)
                                {
+                                       ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT,
+                                                       "m_ssl_gnutls.so: Error while writing to fd %d: %s",
+                                                       session->fd, gnutls_strerror(ret));
                                        CloseSession(session);
                                }
                                else
@@ -631,7 +652,7 @@ class ModuleSSLGnuTLS : public Module
                        return;
 
                // Bugfix, only send this numeric for *our* SSL users
-               if (dest->GetExt("ssl", dummy) || ((IS_LOCAL(dest) && (dest->io == this))))
+               if (dest->GetExt("ssl", dummy) || ((IS_LOCAL(dest) && (dest->GetIOHook() == this))))
                {
                        ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick.c_str(), dest->nick.c_str());
                }
@@ -691,6 +712,9 @@ class ModuleSSLGnuTLS : public Module
                        else
                        {
                                // Handshake failed.
+                               ServerInstance->Logs->Log("m_ssl_gnutls", DEFAULT,
+                                               "m_ssl_gnutls.so: Handshake failed on fd %d: %s",
+                                               session->fd, gnutls_strerror(ret));
                                CloseSession(session);
                                session->status = ISSL_CLOSING;
                        }
@@ -722,7 +746,7 @@ class ModuleSSLGnuTLS : public Module
        {
                // This occurs AFTER OnUserConnect so we can be sure the
                // protocol module has propagated the NICK message.
-               if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
+               if (user->GetIOHook() == this && (IS_LOCAL(user)))
                {
                        // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
                        ServerInstance->PI->SendMetaData(user, TYPE_USER, "SSL", "on");
@@ -748,7 +772,7 @@ class ModuleSSLGnuTLS : public Module
 
        virtual void OnBufferFlushed(User* user)
        {
-               if (user->GetExt("ssl"))
+               if (user->GetIOHook() == this)
                {
                        issl_session* session = &sessions[user->GetFd()];
                        if (session && session->outbuf.size())
@@ -892,7 +916,7 @@ class ModuleSSLGnuTLS : public Module
 
                /* Beware here we do not check for errors.
                 */
-               if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0)))
+               if ((gnutls_x509_crt_get_expiration_time(cert) < ServerInstance->Time()) || (gnutls_x509_crt_get_activation_time(cert) > ServerInstance->Time()))
                {
                        certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
                }
@@ -907,7 +931,7 @@ class ModuleSSLGnuTLS : public Module
                GenericCapHandler(ev, "tls", "tls");
        }
 
-        void Prioritize()
+       void Prioritize()
        {
                Module* server = ServerInstance->Modules->Find("m_spanningtree.so");
                ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIO_AFTER, &server);