]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_gnutls.cpp
'svn propset -R svn:eol-style CR *' Set to UNIX-style always. Binaries are auto skipp...
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_gnutls.cpp
1 /*       +------------------------------------+\r *       | Inspire Internet Relay Chat Daemon |\r *       +------------------------------------+\r *\r *  InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r *            the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#include "inspircd.h"\r\r#include <gnutls/gnutls.h>\r#include <gnutls/x509.h>\r\r#include "inspircd_config.h"\r#include "configreader.h"\r#include "users.h"\r#include "channels.h"\r#include "modules.h"\r#include "socket.h"\r#include "hashcomp.h"\r#include "transport.h"\r\r#ifdef WINDOWS\r#pragma comment(lib, "libgnutls-13.lib")\r#undef MAX_DESCRIPTORS\r#define MAX_DESCRIPTORS 10000\r#endif\r\r/* $ModDesc: Provides SSL support for clients */\r/* $CompileFlags: exec("libgnutls-config --cflags") */\r/* $LinkerFlags: rpath("libgnutls-config --libs") exec("libgnutls-config --libs") */\r/* $ModDep: transport.h */\r\r\renum issl_status { ISSL_NONE, ISSL_HANDSHAKING_READ, ISSL_HANDSHAKING_WRITE, ISSL_HANDSHAKEN, ISSL_CLOSING, ISSL_CLOSED };\r\rbool isin(int port, const std::vector<int> &portlist)\r{\r      for(unsigned int i = 0; i < portlist.size(); i++)\r              if(portlist[i] == port)\r                        return true;\r\r  return false;\r}\r\r/** Represents an SSL user's extra data\r */\rclass issl_session : public classbase\r{\rpublic:\r   gnutls_session_t sess;\r issl_status status;\r    std::string outbuf;\r    int inbufoffset;\r       char* inbuf;\r   int fd;\r};\r\rclass ModuleSSLGnuTLS : public Module\r{\r\r   ConfigReader* Conf;\r\r   char* dummy;\r\r  std::vector<int> listenports;\r\r int inbufsize;\r issl_session sessions[MAX_DESCRIPTORS];\r\r       gnutls_certificate_credentials x509_cred;\r      gnutls_dh_params dh_params;\r\r   std::string keyfile;\r   std::string certfile;\r  std::string cafile;\r    std::string crlfile;\r   std::string sslports;\r  int dh_bits;\r\r  int clientactive;\r\r public:\r\r   ModuleSSLGnuTLS(InspIRCd* Me)\r          : Module(Me)\r   {\r              ServerInstance->PublishInterface("InspSocketHook", this);\r\r             // Not rehashable...because I cba to reduce all the sizes of existing buffers.\r         inbufsize = ServerInstance->Config->NetBufferSize;\r\r            gnutls_global_init(); // This must be called once in the program\r\r              if(gnutls_certificate_allocate_credentials(&x509_cred) != 0)\r                   ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to allocate certificate credentials");\r\r          // Guessing return meaning\r             if(gnutls_dh_params_init(&dh_params) < 0)\r                      ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to initialise DH parameters");\r\r          // Needs the flag as it ignores a plain /rehash\r                OnRehash(NULL,"ssl");\r\r         // Void return, guess we assume success\r                gnutls_certificate_set_dh_params(x509_cred, dh_params);\r        }\r\r     virtual void OnRehash(userrec* user, const std::string &param)\r {\r              if(param != "ssl")\r                     return;\r\r               Conf = new ConfigReader(ServerInstance);\r\r              for(unsigned int i = 0; i < listenports.size(); i++)\r           {\r                      ServerInstance->Config->DelIOHook(listenports[i]);\r             }\r\r             listenports.clear();\r           clientactive = 0;\r              sslports.clear();\r\r             for(int i = 0; i < Conf->Enumerate("bind"); i++)\r               {\r                      // For each <bind> tag\r                 std::string x = Conf->ReadValue("bind", "type", i);\r                    if(((x.empty()) || (x == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "gnutls"))\r                       {\r                              // Get the port we're meant to be listening on with SSL\r                                std::string port = Conf->ReadValue("bind", "port", i);\r                         irc::portparser portrange(port, false);\r                                long portno = -1;\r                              while ((portno = portrange.GetToken()))\r                                {\r                                      clientactive++;\r                                        try\r                                    {\r                                              if (ServerInstance->Config->AddIOHook(portno, this))\r                                           {\r                                                      listenports.push_back(portno);\r                                                 for (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)\r                                                              if (ServerInstance->Config->ports[i]->GetPort() == portno)\r                                                                     ServerInstance->Config->ports[i]->SetDescription("ssl");\r                                                       ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Enabling SSL for port %d", portno);\r                                                     sslports.append("*:").append(ConvToStr(portno)).append(";");\r                                           }\r                                              else\r                                           {\r                                                      ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno);\r                                                }\r                                      }\r                                      catch (ModuleException &e)\r                                     {\r                                              ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: FAILED to enable SSL on port %d: %s. Maybe it's already hooked by the same port on a different IP, or you have an other SSL or similar module loaded?", portno, e.GetReason());\r                                 }\r                              }\r                      }\r              }\r\r             std::string confdir(ServerInstance->ConfigFileName);\r           // +1 so we the path ends with a /\r             confdir = confdir.substr(0, confdir.find_last_of('/') + 1);\r\r           cafile  = Conf->ReadValue("gnutls", "cafile", 0);\r              crlfile = Conf->ReadValue("gnutls", "crlfile", 0);\r             certfile        = Conf->ReadValue("gnutls", "certfile", 0);\r            keyfile = Conf->ReadValue("gnutls", "keyfile", 0);\r             dh_bits = Conf->ReadInteger("gnutls", "dhbits", 0, false);\r\r            // Set all the default values needed.\r          if (cafile.empty())\r                    cafile = "ca.pem";\r\r            if (crlfile.empty())\r                   crlfile = "crl.pem";\r\r          if (certfile.empty())\r                  certfile = "cert.pem";\r\r                if (keyfile.empty())\r                   keyfile = "key.pem";\r\r          if((dh_bits != 768) && (dh_bits != 1024) && (dh_bits != 2048) && (dh_bits != 3072) && (dh_bits != 4096))\r                       dh_bits = 1024;\r\r               // Prepend relative paths with the path to the config directory.\r               if(cafile[0] != '/')\r                   cafile = confdir + cafile;\r\r            if(crlfile[0] != '/')\r                  crlfile = confdir + crlfile;\r\r          if(certfile[0] != '/')\r                 certfile = confdir + certfile;\r\r                if(keyfile[0] != '/')\r                  keyfile = confdir + keyfile;\r\r          int ret;\r\r              if((ret =gnutls_certificate_set_x509_trust_file(x509_cred, cafile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)\r                  ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 trust file '%s': %s", cafile.c_str(), gnutls_strerror(ret));\r\r               if((ret = gnutls_certificate_set_x509_crl_file (x509_cred, crlfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)\r                 ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to set X.509 CRL file '%s': %s", crlfile.c_str(), gnutls_strerror(ret));\r\r                if((ret = gnutls_certificate_set_x509_key_file (x509_cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM)) < 0)\r               {\r                      // If this fails, no SSL port will work. At all. So, do the smart thing - throw a ModuleException\r                      throw ModuleException("Unable to load GnuTLS server certificate: " + std::string(gnutls_strerror(ret)));\r               }\r\r             // This may be on a large (once a day or week) timer eventually.\r               GenerateDHParams();\r\r           DELETE(Conf);\r  }\r\r     void GenerateDHParams()\r        {\r              // Generate Diffie Hellman parameters - for use with DHE\r               // kx algorithms. These should be discarded and regenerated\r            // once a day, once a week or once a month. Depending on the\r           // security requirements.\r\r             int ret;\r\r              if((ret = gnutls_dh_params_generate2(dh_params, dh_bits)) < 0)\r                 ServerInstance->Log(DEFAULT, "m_ssl_gnutls.so: Failed to generate DH parameters (%d bits): %s", dh_bits, gnutls_strerror(ret));\r        }\r\r     virtual ~ModuleSSLGnuTLS()\r     {\r              gnutls_dh_params_deinit(dh_params);\r            gnutls_certificate_free_credentials(x509_cred);\r                gnutls_global_deinit();\r        }\r\r     virtual void OnCleanup(int target_type, void* item)\r    {\r              if(target_type == TYPE_USER)\r           {\r                      userrec* user = (userrec*)item;\r\r                       if(user->GetExt("ssl", dummy) && isin(user->GetPort(), listenports))\r                   {\r                              // User is using SSL, they're a local user, and they're using one of *our* SSL ports.\r                          // Potentially there could be multiple SSL modules loaded at once on different ports.\r                          ServerInstance->GlobalCulls.AddItem(user, "SSL module unloading");\r                     }\r                      if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports))\r                     {\r                              ssl_cert* tofree;\r                              user->GetExt("ssl_cert", tofree);\r                              delete tofree;\r                         user->Shrink("ssl_cert");\r                      }\r              }\r      }\r\r     virtual void OnUnloadModule(Module* mod, const std::string &name)\r      {\r              if(mod == this)\r                {\r                      for(unsigned int i = 0; i < listenports.size(); i++)\r                   {\r                              ServerInstance->Config->DelIOHook(listenports[i]);\r                             for (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)\r                                      if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])\r                                             ServerInstance->Config->ports[j]->SetDescription("plaintext");\r                 }\r              }\r      }\r\r     virtual Version GetVersion()\r   {\r              return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);\r    }\r\r     void Implements(char* List)\r    {\r              List[I_On005Numeric] = List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1;\r         List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;\r   }\r\r     virtual void On005Numeric(std::string &output)\r {\r              output.append(" SSL=" + sslports);\r     }\r\r     virtual char* OnRequest(Request* request)\r      {\r              ISHRequest* ISR = (ISHRequest*)request;\r                if (strcmp("IS_NAME", request->GetId()) == 0)\r          {\r                      return "gnutls";\r               }\r              else if (strcmp("IS_HOOK", request->GetId()) == 0)\r             {\r                      char* ret = "OK";\r                      try\r                    {\r                              ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;\r                   }\r                      catch (ModuleException &e)\r                     {\r                              return NULL;\r                   }\r                      return ret;\r            }\r              else if (strcmp("IS_UNHOOK", request->GetId()) == 0)\r           {\r                      return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;\r         }\r              else if (strcmp("IS_HSDONE", request->GetId()) == 0)\r           {\r                      if (ISR->Sock->GetFd() < 0)\r                            return (char*)"OK";\r\r                   issl_session* session = &sessions[ISR->Sock->GetFd()];\r                 return (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) ? NULL : (char*)"OK";\r           }\r              else if (strcmp("IS_ATTACH", request->GetId()) == 0)\r           {\r                      if (ISR->Sock->GetFd() > -1)\r                   {\r                              issl_session* session = &sessions[ISR->Sock->GetFd()];\r                         if (session->sess)\r                             {\r                                      if ((Extensible*)ServerInstance->FindDescriptor(ISR->Sock->GetFd()) == (Extensible*)(ISR->Sock))\r                                       {\r                                              VerifyCertificate(session, (InspSocket*)ISR->Sock);\r                                            return "OK";\r                                   }\r                              }\r                      }\r              }\r              return NULL;\r   }\r\r\r    virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)\r   {\r              issl_session* session = &sessions[fd];\r\r                session->fd = fd;\r              session->inbuf = new char[inbufsize];\r          session->inbufoffset = 0;\r\r             gnutls_init(&session->sess, GNUTLS_SERVER);\r\r           gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.\r                gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);\r              gnutls_dh_set_prime_bits(session->sess, dh_bits);\r\r             /* This is an experimental change to avoid a warning on 64bit systems about casting between integer and pointer of different sizes\r              * This needs testing, but it's easy enough to rollback if need be\r              * Old: gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.\r             * New: gnutls_transport_set_ptr(session->sess, &fd); // Give gnutls the fd for the socket.\r             *\r              * With testing this seems to...not work :/\r             */\r\r           gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.\r\r           gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any.\r\r              Handshake(session);\r    }\r\r     virtual void OnRawSocketConnect(int fd)\r        {\r              issl_session* session = &sessions[fd];\r\r                session->fd = fd;\r              session->inbuf = new char[inbufsize];\r          session->inbufoffset = 0;\r\r             gnutls_init(&session->sess, GNUTLS_CLIENT);\r\r           gnutls_set_default_priority(session->sess); // Avoid calling all the priority functions, defaults are adequate.\r                gnutls_credentials_set(session->sess, GNUTLS_CRD_CERTIFICATE, x509_cred);\r              gnutls_dh_set_prime_bits(session->sess, dh_bits);\r              gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket.\r\r           Handshake(session);\r    }\r\r     virtual void OnRawSocketClose(int fd)\r  {\r              CloseSession(&sessions[fd]);\r\r          EventHandler* user = ServerInstance->SE->GetRef(fd);\r\r          if ((user) && (user->GetExt("ssl_cert", dummy)))\r               {\r                      ssl_cert* tofree;\r                      user->GetExt("ssl_cert", tofree);\r                      delete tofree;\r                 user->Shrink("ssl_cert");\r              }\r      }\r\r     virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)\r {\r              issl_session* session = &sessions[fd];\r\r                if (!session->sess)\r            {\r                      readresult = 0;\r                        CloseSession(session);\r                 return 1;\r              }\r\r             if (session->status == ISSL_HANDSHAKING_READ)\r          {\r                      // The handshake isn't finished, try to finish it.\r\r                    if(!Handshake(session))\r                        {\r                              // Couldn't resume handshake.\r                          return -1;\r                     }\r              }\r              else if (session->status == ISSL_HANDSHAKING_WRITE)\r            {\r                      errno = EAGAIN;\r                        return -1;\r             }\r\r             // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN.\r\r          if (session->status == ISSL_HANDSHAKEN)\r                {\r                      // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.\r                        // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.\r                 int ret = gnutls_record_recv(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);\r\r                 if (ret == 0)\r                  {\r                              // Client closed connection.\r                           readresult = 0;\r                                CloseSession(session);\r                         return 1;\r                      }\r                      else if (ret < 0)\r                      {\r                              if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)\r                              {\r                                      errno = EAGAIN;\r                                        return -1;\r                             }\r                              else\r                           {\r                                      readresult = 0;\r                                        CloseSession(session);\r                         }\r                      }\r                      else\r                   {\r                              // Read successfully 'ret' bytes into inbuf + inbufoffset\r                              // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'\r                            // 'buffer' is 'count' long\r\r                           unsigned int length = ret + session->inbufoffset;\r\r                             if(count <= length)\r                            {\r                                      memcpy(buffer, session->inbuf, count);\r                                 // Move the stuff left in inbuf to the beginning of it\r                                 memcpy(session->inbuf, session->inbuf + count, (length - count));\r                                      // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.\r                                   session->inbufoffset = length - count;\r                                 // Insp uses readresult as the count of how much data there is in buffer, so:\r                                  readresult = count;\r                            }\r                              else\r                           {\r                                      // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.\r                                     memcpy(buffer, session->inbuf, length);\r                                        // Zero the offset, as there's nothing there..\r                                 session->inbufoffset = 0;\r                                      // As above\r                                    readresult = length;\r                           }\r                      }\r              }\r              else if(session->status == ISSL_CLOSING)\r                       readresult = 0;\r\r               return 1;\r      }\r\r     virtual int OnRawSocketWrite(int fd, const char* buffer, int count)\r    {\r              if (!count)\r                    return 0;\r\r             issl_session* session = &sessions[fd];\r         const char* sendbuffer = buffer;\r\r              if (!session->sess)\r            {\r                      ServerInstance->Log(DEBUG,"No session");\r                       CloseSession(session);\r                 return 1;\r              }\r\r             session->outbuf.append(sendbuffer, count);\r             sendbuffer = session->outbuf.c_str();\r          count = session->outbuf.size();\r\r               if (session->status == ISSL_HANDSHAKING_WRITE)\r         {\r                      // The handshake isn't finished, try to finish it.\r                     ServerInstance->Log(DEBUG,"Finishing handshake");\r                      Handshake(session);\r                    errno = EAGAIN;\r                        return -1;\r             }\r\r             int ret = 0;\r\r          if (session->status == ISSL_HANDSHAKEN)\r                {\r                      ServerInstance->Log(DEBUG,"Send record");\r                      ret = gnutls_record_send(session->sess, sendbuffer, count);\r                    ServerInstance->Log(DEBUG,"Return: %d", ret);\r\r                 if (ret == 0)\r                  {\r                              CloseSession(session);\r                 }\r                      else if (ret < 0)\r                      {\r                              if(ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED)\r                               {\r                                      ServerInstance->Log(DEBUG,"Not egain or interrupt, close session");\r                                    CloseSession(session);\r                         }\r                              else\r                           {\r                                      ServerInstance->Log(DEBUG,"Again please");\r                                     errno = EAGAIN;\r                                        return -1;\r                             }\r                      }\r                      else\r                   {\r                              ServerInstance->Log(DEBUG,"Trim buffer");\r                              session->outbuf = session->outbuf.substr(ret);\r                 }\r              }\r\r             /* Who's smart idea was it to return 1 when we havent written anything?\r                 * This fucks the buffer up in InspSocket :p\r            */\r            return ret < 1 ? 0 : ret;\r      }\r\r     // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection\r virtual void OnWhois(userrec* source, userrec* dest)\r   {\r              if (!clientactive)\r                     return;\r\r               // Bugfix, only send this numeric for *our* SSL users\r          if(dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) &&  isin(dest->GetPort(), listenports)))\r              {\r                      ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);\r             }\r      }\r\r     virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)\r      {\r              // check if the linking module wants to know about OUR metadata\r                if(extname == "ssl")\r           {\r                      // check if this user has an swhois field to send\r                      if(user->GetExt(extname, dummy))\r                       {\r                              // call this function in the linking module, let it format the data how it\r                             // sees fit, and send it on its way. We dont need or want to know how.\r                         proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");\r                    }\r              }\r      }\r\r     virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)\r   {\r              // check if its our metadata key, and its associated with a user\r               if ((target_type == TYPE_USER) && (extname == "ssl"))\r          {\r                      userrec* dest = (userrec*)target;\r                      // if they dont already have an ssl flag, accept the remote server's\r                   if (!dest->GetExt(extname, dummy))\r                     {\r                              dest->Extend(extname, "ON");\r                   }\r              }\r      }\r\r     bool Handshake(issl_session* session)\r  {\r              int ret = gnutls_handshake(session->sess);\r\r            if (ret < 0)\r           {\r                      if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)\r                       {\r                              // Handshake needs resuming later, read() or write() would have blocked.\r\r                              if(gnutls_record_get_direction(session->sess) == 0)\r                            {\r                                      // gnutls_handshake() wants to read() again.\r                                   session->status = ISSL_HANDSHAKING_READ;\r                               }\r                              else\r                           {\r                                      // gnutls_handshake() wants to write() again.\r                                  session->status = ISSL_HANDSHAKING_WRITE;\r                                      MakePollWrite(session);\r                                }\r                      }\r                      else\r                   {\r                              // Handshake failed.\r                           CloseSession(session);\r                         session->status = ISSL_CLOSING;\r                        }\r\r                     return false;\r          }\r              else\r           {\r                      // Handshake complete.\r                 // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.\r                     userrec* extendme = ServerInstance->FindDescriptor(session->fd);\r                       if (extendme)\r                  {\r                              if (!extendme->GetExt("ssl", dummy))\r                                   extendme->Extend("ssl", "ON");\r                 }\r\r                     // Change the seesion state\r                    session->status = ISSL_HANDSHAKEN;\r\r                    // Finish writing, if any left\r                 MakePollWrite(session);\r\r                       return true;\r           }\r      }\r\r     virtual void OnPostConnect(userrec* user)\r      {\r              // This occurs AFTER OnUserConnect so we can be sure the\r               // protocol module has propogated the NICK message.\r            if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))\r          {\r                      // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.\r                        std::deque<std::string>* metadata = new std::deque<std::string>;\r                       metadata->push_back(user->nick);\r                       metadata->push_back("ssl");             // The metadata id\r                     metadata->push_back("ON");              // The value to send\r                   Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");\r                       event->Send(ServerInstance);            // Trigger the event. We don't care what module picks it up.\r                   DELETE(event);\r                 DELETE(metadata);\r\r                     VerifyCertificate(&sessions[user->GetFd()],user);\r                      if (sessions[user->GetFd()].sess)\r                      {\r                              std::string cipher = gnutls_kx_get_name(gnutls_kx_get(sessions[user->GetFd()].sess));\r                          cipher.append("-").append(gnutls_cipher_get_name(gnutls_cipher_get(sessions[user->GetFd()].sess))).append("-");\r                                cipher.append(gnutls_mac_get_name(gnutls_mac_get(sessions[user->GetFd()].sess)));\r                              user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, cipher.c_str());\r                       }\r              }\r      }\r\r     void MakePollWrite(issl_session* session)\r      {\r              OnRawSocketWrite(session->fd, NULL, 0);\r        }\r\r     void CloseSession(issl_session* session)\r       {\r              if(session->sess)\r              {\r                      gnutls_bye(session->sess, GNUTLS_SHUT_WR);\r                     gnutls_deinit(session->sess);\r          }\r\r             if(session->inbuf)\r             {\r                      delete[] session->inbuf;\r               }\r\r             session->outbuf.clear();\r               session->inbuf = NULL;\r         session->sess = NULL;\r          session->status = ISSL_NONE;\r   }\r\r     void VerifyCertificate(issl_session* session, Extensible* user)\r        {\r              if (!session->sess || !user)\r                   return;\r\r               unsigned int status;\r           const gnutls_datum_t* cert_list;\r               int ret;\r               unsigned int cert_list_size;\r           gnutls_x509_crt_t cert;\r                char name[MAXBUF];\r             unsigned char digest[MAXBUF];\r          size_t digest_size = sizeof(digest);\r           size_t name_size = sizeof(name);\r               ssl_cert* certinfo = new ssl_cert;\r\r            user->Extend("ssl_cert",certinfo);\r\r            /* This verification function uses the trusted CAs in the credentials\r           * structure. So you must have installed one or more CA certificates.\r           */\r            ret = gnutls_certificate_verify_peers2(session->sess, &status);\r\r               if (ret < 0)\r           {\r                      certinfo->data.insert(std::make_pair("error",std::string(gnutls_strerror(ret))));\r                      return;\r                }\r\r             if (status & GNUTLS_CERT_INVALID)\r              {\r                      certinfo->data.insert(std::make_pair("invalid",ConvToStr(1)));\r         }\r              else\r           {\r                      certinfo->data.insert(std::make_pair("invalid",ConvToStr(0)));\r         }\r              if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)\r             {\r                      certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));\r           }\r              else\r           {\r                      certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));\r           }\r              if (status & GNUTLS_CERT_REVOKED)\r              {\r                      certinfo->data.insert(std::make_pair("revoked",ConvToStr(1)));\r         }\r              else\r           {\r                      certinfo->data.insert(std::make_pair("revoked",ConvToStr(0)));\r         }\r              if (status & GNUTLS_CERT_SIGNER_NOT_CA)\r                {\r                      certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));\r         }\r              else\r           {\r                      certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));\r         }\r\r             /* Up to here the process is the same for X.509 certificates and\r                * OpenPGP keys. From now on X.509 certificates are assumed. This can\r           * be easily extended to work with openpgp keys as well.\r                */\r            if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509)\r             {\r                      certinfo->data.insert(std::make_pair("error","No X509 keys sent"));\r                    return;\r                }\r\r             ret = gnutls_x509_crt_init(&cert);\r             if (ret < 0)\r           {\r                      certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));\r                   return;\r                }\r\r             cert_list_size = 0;\r            cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size);\r              if (cert_list == NULL)\r         {\r                      certinfo->data.insert(std::make_pair("error","No certificate was found"));\r                     return;\r                }\r\r             /* This is not a real world example, since we only check the first\r              * certificate in the given chain.\r              */\r\r           ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);\r                if (ret < 0)\r           {\r                      certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));\r                   return;\r                }\r\r             gnutls_x509_crt_get_dn(cert, name, &name_size);\r\r               certinfo->data.insert(std::make_pair("dn",name));\r\r             gnutls_x509_crt_get_issuer_dn(cert, name, &name_size);\r\r                certinfo->data.insert(std::make_pair("issuer",name));\r\r         if ((ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, digest, &digest_size)) < 0)\r           {\r                      certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret)));\r           }\r              else\r           {\r                      certinfo->data.insert(std::make_pair("fingerprint",irc::hex(digest, digest_size)));\r            }\r\r             /* Beware here we do not check for errors.\r              */\r            if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0)))\r            {\r                      certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));\r                }\r\r             gnutls_x509_crt_deinit(cert);\r\r         return;\r        }\r\r};\r\rMODULE_INIT(ModuleSSLGnuTLS);\r\r