]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
Fix broken modules (broken due to api change)
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_openssl.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include <string>
15 #include <vector>
16
17 #include <openssl/ssl.h>
18 #include <openssl/err.h>
19
20 #include "inspircd_config.h"
21 #include "configreader.h"
22 #include "users.h"
23 #include "channels.h"
24 #include "modules.h"
25
26 #include "socket.h"
27 #include "hashcomp.h"
28 #include "inspircd.h"
29
30 #include "transport.h"
31
32 /* $ModDesc: Provides SSL support for clients */
33 /* $CompileFlags: pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
34 /* $LinkerFlags: rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
35 /* $ModDep: transport.h */
36
37 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
38 enum issl_io_status { ISSL_WRITE, ISSL_READ };
39
40 static bool SelfSigned = false;
41
42 bool isin(int port, const std::vector<int> &portlist)
43 {
44         for(unsigned int i = 0; i < portlist.size(); i++)
45                 if(portlist[i] == port)
46                         return true;
47
48         return false;
49 }
50
51 char* get_error()
52 {
53         return ERR_error_string(ERR_get_error(), NULL);
54 }
55
56 static int error_callback(const char *str, size_t len, void *u);
57
58 /** Represents an SSL user's extra data
59  */
60 class issl_session : public classbase
61 {
62 public:
63         SSL* sess;
64         issl_status status;
65         issl_io_status rstat;
66         issl_io_status wstat;
67
68         unsigned int inbufoffset;
69         char* inbuf;                    // Buffer OpenSSL reads into.
70         std::string outbuf;     // Buffer for outgoing data that OpenSSL will not take.
71         int fd;
72         bool outbound;
73
74         issl_session()
75         {
76                 outbound = false;
77                 rstat = ISSL_READ;
78                 wstat = ISSL_WRITE;
79         }
80 };
81
82 static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
83 {
84         /* XXX: This will allow self signed certificates.
85          * In the future if we want an option to not allow this,
86          * we can just return preverify_ok here, and openssl
87          * will boot off self-signed and invalid peer certs.
88          */
89         int ve = X509_STORE_CTX_get_error(ctx);
90
91         SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
92
93         return 1;
94 }
95
96 class ModuleSSLOpenSSL : public Module
97 {
98
99         ConfigReader* Conf;
100
101         std::vector<int> listenports;
102
103         int inbufsize;
104         issl_session sessions[MAX_DESCRIPTORS];
105
106         SSL_CTX* ctx;
107         SSL_CTX* clictx;
108
109         char* dummy;
110         char cipher[MAXBUF];
111
112         std::string keyfile;
113         std::string certfile;
114         std::string cafile;
115         // std::string crlfile;
116         std::string dhfile;
117
118         int clientactive;
119
120  public:
121
122         InspIRCd* PublicInstance;
123
124         ModuleSSLOpenSSL(InspIRCd* Me)
125                 : Module::Module(Me), PublicInstance(Me)
126         {
127                 ServerInstance->PublishInterface("InspSocketHook", this);
128
129                 // Not rehashable...because I cba to reduce all the sizes of existing buffers.
130                 inbufsize = ServerInstance->Config->NetBufferSize;
131
132                 /* Global SSL library initialization*/
133                 SSL_library_init();
134                 SSL_load_error_strings();
135
136                 /* Build our SSL contexts:
137                  * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
138                  */
139                 ctx = SSL_CTX_new( SSLv23_server_method() );
140                 clictx = SSL_CTX_new( SSLv23_client_method() );
141
142                 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
143                 SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
144
145                 // Needs the flag as it ignores a plain /rehash
146                 OnRehash(NULL,"ssl");
147         }
148
149         virtual void OnRehash(userrec* user, const std::string &param)
150         {
151                 if (param != "ssl")
152                         return;
153
154                 Conf = new ConfigReader(ServerInstance);
155
156                 for (unsigned int i = 0; i < listenports.size(); i++)
157                 {
158                         ServerInstance->Config->DelIOHook(listenports[i]);
159                 }
160
161                 listenports.clear();
162                 clientactive = 0;
163
164                 for (int i = 0; i < Conf->Enumerate("bind"); i++)
165                 {
166                         // For each <bind> tag
167                         if (((Conf->ReadValue("bind", "type", i) == "") || (Conf->ReadValue("bind", "type", i) == "clients")) && (Conf->ReadValue("bind", "ssl", i) == "openssl"))
168                         {
169                                 // Get the port we're meant to be listening on with SSL
170                                 std::string port = Conf->ReadValue("bind", "port", i);
171                                 irc::portparser portrange(port, false);
172                                 long portno = -1;
173                                 while ((portno = portrange.GetToken()))
174                                 {
175                                         clientactive++;
176                                         try
177                                         {
178                                                 if (ServerInstance->Config->AddIOHook(portno, this))
179                                                 {
180                                                         listenports.push_back(portno);
181                                                                 for (unsigned int i = 0; i < ServerInstance->stats->BoundPortCount; i++)
182                                                                 if (ServerInstance->Config->ports[i] == portno)
183                                                                         ServerInstance->Config->openSockfd[i]->SetDescription("ssl");
184                                                         ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %d", portno);
185                                                 }
186                                                 else
187                                                 {
188                                                         ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: FAILED to enable SSL on port %d, maybe you have another ssl or similar module loaded?", portno);
189                                                 }
190                                         }
191                                         catch (ModuleException &e)
192                                         {
193                                                 ServerInstance->Log(DEFAULT, "m_ssl_openssl.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 another SSL or similar module loaded?", portno, e.GetReason());
194                                         }
195                                 }
196                         }
197                 }
198
199                 std::string confdir(ServerInstance->ConfigFileName);
200                 // +1 so we the path ends with a /
201                 confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
202
203                 cafile   = Conf->ReadValue("openssl", "cafile", 0);
204                 certfile = Conf->ReadValue("openssl", "certfile", 0);
205                 keyfile  = Conf->ReadValue("openssl", "keyfile", 0);
206                 dhfile   = Conf->ReadValue("openssl", "dhfile", 0);
207
208                 // Set all the default values needed.
209                 if (cafile == "")
210                         cafile = "ca.pem";
211
212                 if (certfile == "")
213                         certfile = "cert.pem";
214
215                 if (keyfile == "")
216                         keyfile = "key.pem";
217
218                 if (dhfile == "")
219                         dhfile = "dhparams.pem";
220
221                 // Prepend relative paths with the path to the config directory.
222                 if (cafile[0] != '/')
223                         cafile = confdir + cafile;
224
225                 //if(crlfile[0] != '/')
226                 //      crlfile = confdir + crlfile;
227
228                 if (certfile[0] != '/')
229                         certfile = confdir + certfile;
230
231                 if (keyfile[0] != '/')
232                         keyfile = confdir + keyfile;
233
234                 if (dhfile[0] != '/')
235                         dhfile = confdir + dhfile;
236
237                 /* Load our keys and certificates
238                  * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
239                  */
240                 if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
241                 {
242                         ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
243                         ERR_print_errors_cb(error_callback, this);
244                 }
245
246                 if (((!SSL_CTX_use_PrivateKey_file(ctx, keyfile.c_str(), SSL_FILETYPE_PEM))) || (!SSL_CTX_use_PrivateKey_file(clictx, keyfile.c_str(), SSL_FILETYPE_PEM)))
247                 {
248                         ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
249                         ERR_print_errors_cb(error_callback, this);
250                 }
251
252                 /* Load the CAs we trust*/
253                 if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
254                 {
255                         ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno));
256                         ERR_print_errors_cb(error_callback, this);
257                 }
258
259                 FILE* dhpfile = fopen(dhfile.c_str(), "r");
260                 DH* ret;
261
262                 if (dhpfile == NULL)
263                 {
264                         ServerInstance->Log(DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
265                         throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
266                 }
267                 else
268                 {
269                         ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
270                         if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
271                         {
272                                 ServerInstance->Log(DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
273                                 ERR_print_errors_cb(error_callback, this);
274                         }
275                 }
276
277                 fclose(dhpfile);
278
279                 DELETE(Conf);
280         }
281
282         virtual ~ModuleSSLOpenSSL()
283         {
284                 SSL_CTX_free(ctx);
285                 SSL_CTX_free(clictx);
286         }
287
288         virtual void OnCleanup(int target_type, void* item)
289         {
290                 if (target_type == TYPE_USER)
291                 {
292                         userrec* user = (userrec*)item;
293
294                         if (user->GetExt("ssl", dummy) && IS_LOCAL(user) && isin(user->GetPort(), listenports))
295                         {
296                                 // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
297                                 // Potentially there could be multiple SSL modules loaded at once on different ports.
298                                 ServerInstance->GlobalCulls.AddItem(user, "SSL module unloading");
299                         }
300                         if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports))
301                         {
302                                 ssl_cert* tofree;
303                                 user->GetExt("ssl_cert", tofree);
304                                 delete tofree;
305                                 user->Shrink("ssl_cert");
306                         }
307                 }
308         }
309
310         virtual void OnUnloadModule(Module* mod, const std::string &name)
311         {
312                 if (mod == this)
313                 {
314                         for(unsigned int i = 0; i < listenports.size(); i++)
315                         {
316                                 ServerInstance->Config->DelIOHook(listenports[i]);
317                                 for (unsigned int j = 0; j < ServerInstance->stats->BoundPortCount; j++)
318                                         if (ServerInstance->Config->ports[j] == listenports[i])
319                                                 if (ServerInstance->Config->openSockfd[j])
320                                                         ServerInstance->Config->openSockfd[j]->SetDescription("plaintext");
321                         }
322                 }
323         }
324
325         virtual Version GetVersion()
326         {
327                 return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
328         }
329
330         void Implements(char* List)
331         {
332                 List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1;
333                 List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;
334         }
335
336         virtual char* OnRequest(Request* request)
337         {
338                 ISHRequest* ISR = (ISHRequest*)request;
339                 if (strcmp("IS_NAME", request->GetId()) == 0)
340                 {
341                         return "openssl";
342                 }
343                 else if (strcmp("IS_HOOK", request->GetId()) == 0)
344                 {
345                         char* ret = "OK";
346                         try
347                         {
348                                 ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
349                         }
350                         catch (ModuleException &e)
351                         {
352                                 return NULL;
353                         }
354
355                         return ret;
356                 }
357                 else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
358                 {
359                         return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
360                 }
361                 else if (strcmp("IS_HSDONE", request->GetId()) == 0)
362                 {
363                         if (ISR->Sock->GetFd() < 0)
364                                 return (char*)"OK";
365
366                         issl_session* session = &sessions[ISR->Sock->GetFd()];
367                         return (session->status == ISSL_HANDSHAKING) ? NULL : (char*)"OK";
368                 }
369                 else if (strcmp("IS_ATTACH", request->GetId()) == 0)
370                 {
371                         issl_session* session = &sessions[ISR->Sock->GetFd()];
372                         if (session->sess)
373                         {
374                                 VerifyCertificate(session, (InspSocket*)ISR->Sock);
375                                 return "OK";
376                         }
377                 }
378                 return NULL;
379         }
380
381
382         virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
383         {
384                 issl_session* session = &sessions[fd];
385
386                 session->fd = fd;
387                 session->inbuf = new char[inbufsize];
388                 session->inbufoffset = 0;
389                 session->sess = SSL_new(ctx);
390                 session->status = ISSL_NONE;
391                 session->outbound = false;
392
393                 if (session->sess == NULL)
394                         return;
395
396                 if (SSL_set_fd(session->sess, fd) == 0)
397                 {
398                         ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
399                         return;
400                 }
401
402                 Handshake(session);
403         }
404
405         virtual void OnRawSocketConnect(int fd)
406         {
407                 issl_session* session = &sessions[fd];
408
409                 session->fd = fd;
410                 session->inbuf = new char[inbufsize];
411                 session->inbufoffset = 0;
412                 session->sess = SSL_new(clictx);
413                 session->status = ISSL_NONE;
414                 session->outbound = true;
415
416                 if (session->sess == NULL)
417                         return;
418
419                 if (SSL_set_fd(session->sess, fd) == 0)
420                 {
421                         ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
422                         return;
423                 }
424
425                 Handshake(session);
426         }
427
428         virtual void OnRawSocketClose(int fd)
429         {
430                 CloseSession(&sessions[fd]);
431
432                 EventHandler* user = ServerInstance->SE->GetRef(fd);
433
434                 if ((user) && (user->GetExt("ssl_cert", dummy)))
435                 {
436                         ssl_cert* tofree;
437                         user->GetExt("ssl_cert", tofree);
438                         delete tofree;
439                         user->Shrink("ssl_cert");
440                 }
441         }
442
443         virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
444         {
445                 issl_session* session = &sessions[fd];
446
447                 if (!session->sess)
448                 {
449                         readresult = 0;
450                         CloseSession(session);
451                         return 1;
452                 }
453
454                 if (session->status == ISSL_HANDSHAKING)
455                 {
456                         if (session->rstat == ISSL_READ || session->wstat == ISSL_READ)
457                         {
458                                 // The handshake isn't finished and it wants to read, try to finish it.
459                                 if (!Handshake(session))
460                                 {
461                                         // Couldn't resume handshake.
462                                         return -1;
463                                 }
464                         }
465                         else
466                         {
467                                 return -1;
468                         }
469                 }
470
471                 // If we resumed the handshake then session->status will be ISSL_OPEN
472
473                 if (session->status == ISSL_OPEN)
474                 {
475                         if (session->wstat == ISSL_READ)
476                         {
477                                 if(DoWrite(session) == 0)
478                                         return 0;
479                         }
480
481                         if (session->rstat == ISSL_READ)
482                         {
483                                 int ret = DoRead(session);
484
485                                 if (ret > 0)
486                                 {
487                                         if (count <= session->inbufoffset)
488                                         {
489                                                 memcpy(buffer, session->inbuf, count);
490                                                 // Move the stuff left in inbuf to the beginning of it
491                                                 memcpy(session->inbuf, session->inbuf + count, (session->inbufoffset - count));
492                                                 // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
493                                                 session->inbufoffset -= count;
494                                                 // Insp uses readresult as the count of how much data there is in buffer, so:
495                                                 readresult = count;
496                                         }
497                                         else
498                                         {
499                                                 // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
500                                                 memcpy(buffer, session->inbuf, session->inbufoffset);
501
502                                                 readresult = session->inbufoffset;
503                                                 // Zero the offset, as there's nothing there..
504                                                 session->inbufoffset = 0;
505                                         }
506
507                                         return 1;
508                                 }
509                                 else
510                                 {
511                                         return ret;
512                                 }
513                         }
514                 }
515
516                 return -1;
517         }
518
519         virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
520         {
521                 issl_session* session = &sessions[fd];
522
523                 if (!session->sess)
524                 {
525                         CloseSession(session);
526                         return -1;
527                 }
528
529                 session->outbuf.append(buffer, count);
530
531                 if (session->status == ISSL_HANDSHAKING)
532                 {
533                         // The handshake isn't finished, try to finish it.
534                         if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE)
535                                 Handshake(session);
536                 }
537
538                 if (session->status == ISSL_OPEN)
539                 {
540                         if (session->rstat == ISSL_WRITE)
541                                 DoRead(session);
542
543                         if (session->wstat == ISSL_WRITE)
544                                 return DoWrite(session);
545                 }
546
547                 return 1;
548         }
549
550         int DoWrite(issl_session* session)
551         {
552                 if (!session->outbuf.size())
553                         return -1;
554
555                 int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size());
556
557                 if (ret == 0)
558                 {
559                         CloseSession(session);
560                         return 0;
561                 }
562                 else if (ret < 0)
563                 {
564                         int err = SSL_get_error(session->sess, ret);
565
566                         if (err == SSL_ERROR_WANT_WRITE)
567                         {
568                                 session->wstat = ISSL_WRITE;
569                                 return -1;
570                         }
571                         else if (err == SSL_ERROR_WANT_READ)
572                         {
573                                 session->wstat = ISSL_READ;
574                                 return -1;
575                         }
576                         else
577                         {
578                                 CloseSession(session);
579                                 return 0;
580                         }
581                 }
582                 else
583                 {
584                         session->outbuf = session->outbuf.substr(ret);
585                         return ret;
586                 }
587         }
588
589         int DoRead(issl_session* session)
590         {
591                 // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
592                 // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
593
594                 int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
595
596                 if (ret == 0)
597                 {
598                         // Client closed connection.
599                         CloseSession(session);
600                         return 0;
601                 }
602                 else if (ret < 0)
603                 {
604                         int err = SSL_get_error(session->sess, ret);
605
606                         if (err == SSL_ERROR_WANT_READ)
607                         {
608                                 session->rstat = ISSL_READ;
609                                 return -1;
610                         }
611                         else if (err == SSL_ERROR_WANT_WRITE)
612                         {
613                                 session->rstat = ISSL_WRITE;
614                                 return -1;
615                         }
616                         else
617                         {
618                                 CloseSession(session);
619                                 return 0;
620                         }
621                 }
622                 else
623                 {
624                         // Read successfully 'ret' bytes into inbuf + inbufoffset
625                         // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
626                         // 'buffer' is 'count' long
627
628                         session->inbufoffset += ret;
629
630                         return ret;
631                 }
632         }
633
634         // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
635         virtual void OnWhois(userrec* source, userrec* dest)
636         {
637                 if (!clientactive)
638                         return;
639
640                 // Bugfix, only send this numeric for *our* SSL users
641                 if (dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) &&  isin(dest->GetPort(), listenports)))
642                 {
643                         ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
644                 }
645         }
646
647         virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname)
648         {
649                 // check if the linking module wants to know about OUR metadata
650                 if (extname == "ssl")
651                 {
652                         // check if this user has an swhois field to send
653                         if(user->GetExt(extname, dummy))
654                         {
655                                 // call this function in the linking module, let it format the data how it
656                                 // sees fit, and send it on its way. We dont need or want to know how.
657                                 proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, "ON");
658                         }
659                 }
660         }
661
662         virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
663         {
664                 // check if its our metadata key, and its associated with a user
665                 if ((target_type == TYPE_USER) && (extname == "ssl"))
666                 {
667                         userrec* dest = (userrec*)target;
668                         // if they dont already have an ssl flag, accept the remote server's
669                         if (!dest->GetExt(extname, dummy))
670                         {
671                                 dest->Extend(extname, "ON");
672                         }
673                 }
674         }
675
676         bool Handshake(issl_session* session)
677         {
678                 int ret;
679
680                 if (session->outbound)
681                         ret = SSL_connect(session->sess);
682                 else
683                         ret = SSL_accept(session->sess);
684
685                 if (ret < 0)
686                 {
687                         int err = SSL_get_error(session->sess, ret);
688
689                         if (err == SSL_ERROR_WANT_READ)
690                         {
691                                 session->rstat = ISSL_READ;
692                                 session->status = ISSL_HANDSHAKING;
693                         }
694                         else if (err == SSL_ERROR_WANT_WRITE)
695                         {
696                                 session->wstat = ISSL_WRITE;
697                                 session->status = ISSL_HANDSHAKING;
698                                 MakePollWrite(session);
699                         }
700                         else
701                         {
702                                 CloseSession(session);
703                         }
704
705                         return false;
706                 }
707                 else if (ret > 0)
708                 {
709                         // Handshake complete.
710                         // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
711                         userrec* u = ServerInstance->FindDescriptor(session->fd);
712                         if (u)
713                         {
714                                 if (!u->GetExt("ssl", dummy))
715                                         u->Extend("ssl", "ON");
716                         }
717
718                         session->status = ISSL_OPEN;
719
720                         MakePollWrite(session);
721
722                         return true;
723                 }
724                 else if (ret == 0)
725                 {
726                         CloseSession(session);
727                         return true;
728                 }
729
730                 return true;
731         }
732
733         virtual void OnPostConnect(userrec* user)
734         {
735                 // This occurs AFTER OnUserConnect so we can be sure the
736                 // protocol module has propogated the NICK message.
737                 if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
738                 {
739                         // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
740                         std::deque<std::string>* metadata = new std::deque<std::string>;
741                         metadata->push_back(user->nick);
742                         metadata->push_back("ssl");             // The metadata id
743                         metadata->push_back("ON");              // The value to send
744                         Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");
745                         event->Send(ServerInstance);            // Trigger the event. We don't care what module picks it up.
746                         DELETE(event);
747                         DELETE(metadata);
748
749                         VerifyCertificate(&sessions[user->GetFd()], user);
750                         if (sessions[user->GetFd()].sess)
751                                 user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, SSL_get_cipher(sessions[user->GetFd()].sess));
752                 }
753         }
754
755         void MakePollWrite(issl_session* session)
756         {
757                 OnRawSocketWrite(session->fd, NULL, 0);
758         }
759
760         void CloseSession(issl_session* session)
761         {
762                 if (session->sess)
763                 {
764                         SSL_shutdown(session->sess);
765                         SSL_free(session->sess);
766                 }
767
768                 if (session->inbuf)
769                 {
770                         delete[] session->inbuf;
771                 }
772
773                 session->outbuf.clear();
774                 session->inbuf = NULL;
775                 session->sess = NULL;
776                 session->status = ISSL_NONE;
777         }
778
779         void VerifyCertificate(issl_session* session, Extensible* user)
780         {
781                 if (!session->sess || !user)
782                         return;
783
784                 X509* cert;
785                 ssl_cert* certinfo = new ssl_cert;
786                 unsigned int n;
787                 unsigned char md[EVP_MAX_MD_SIZE];
788                 const EVP_MD *digest = EVP_md5();
789
790                 user->Extend("ssl_cert",certinfo);
791
792                 cert = SSL_get_peer_certificate((SSL*)session->sess);
793
794                 if (!cert)
795                 {
796                         certinfo->data.insert(std::make_pair("error","Could not get peer certificate: "+std::string(get_error())));
797                         return;
798                 }
799
800                 certinfo->data.insert(std::make_pair("invalid", SSL_get_verify_result(session->sess) != X509_V_OK ? ConvToStr(1) : ConvToStr(0)));
801
802                 if (SelfSigned)
803                 {
804                         certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));
805                         certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));
806                 }
807                 else
808                 {
809                         certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));
810                         certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));
811                 }
812
813                 certinfo->data.insert(std::make_pair("dn",std::string(X509_NAME_oneline(X509_get_subject_name(cert),0,0))));
814                 certinfo->data.insert(std::make_pair("issuer",std::string(X509_NAME_oneline(X509_get_issuer_name(cert),0,0))));
815
816                 if (!X509_digest(cert, digest, md, &n))
817                 {
818                         certinfo->data.insert(std::make_pair("error","Out of memory generating fingerprint"));
819                 }
820                 else
821                 {
822                         certinfo->data.insert(std::make_pair("fingerprint",irc::hex(md, n)));
823                 }
824
825                 if ((ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(cert), time(NULL)) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_get_notBefore(cert), time(NULL)) == 0))
826                 {
827                         certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
828                 }
829
830                 X509_free(cert);
831         }
832 };
833
834 static int error_callback(const char *str, size_t len, void *u)
835 {
836         ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u;
837         mssl->PublicInstance->Log(DEFAULT, "SSL error: " + std::string(str, len - 1));
838         return 0;
839 }
840
841 class ModuleSSLOpenSSLFactory : public ModuleFactory
842 {
843  public:
844         ModuleSSLOpenSSLFactory()
845         {
846         }
847
848         ~ModuleSSLOpenSSLFactory()
849         {
850         }
851
852         virtual Module * CreateModule(InspIRCd* Me)
853         {
854                 return new ModuleSSLOpenSSL(Me);
855         }
856 };
857
858
859 extern "C" void * init_module( void )
860 {
861         return new ModuleSSLOpenSSLFactory;
862 }