]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
e073eba816b5de32954ce7f3f7b32c70bd8bff55
[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 (size_t i = 0; i < ServerInstance->Config->ports.size(); i++)
182                                                                 if (ServerInstance->Config->ports[i]->GetPort() == portno)
183                                                                         ServerInstance->Config->ports[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 (size_t j = 0; j < ServerInstance->Config->ports.size(); j++)
318                                         if (ServerInstance->Config->ports[j]->GetPort() == listenports[i])
319                                                 ServerInstance->Config->ports[j]->SetDescription("plaintext");
320                         }
321                 }
322         }
323
324         virtual Version GetVersion()
325         {
326                 return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION);
327         }
328
329         void Implements(char* List)
330         {
331                 List[I_OnRawSocketConnect] = List[I_OnRawSocketAccept] = List[I_OnRawSocketClose] = List[I_OnRawSocketRead] = List[I_OnRawSocketWrite] = List[I_OnCleanup] = 1;
332                 List[I_OnRequest] = List[I_OnSyncUserMetaData] = List[I_OnDecodeMetaData] = List[I_OnUnloadModule] = List[I_OnRehash] = List[I_OnWhois] = List[I_OnPostConnect] = 1;
333         }
334
335         virtual char* OnRequest(Request* request)
336         {
337                 ISHRequest* ISR = (ISHRequest*)request;
338                 if (strcmp("IS_NAME", request->GetId()) == 0)
339                 {
340                         return "openssl";
341                 }
342                 else if (strcmp("IS_HOOK", request->GetId()) == 0)
343                 {
344                         char* ret = "OK";
345                         try
346                         {
347                                 ret = ServerInstance->Config->AddIOHook((Module*)this, (InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
348                         }
349                         catch (ModuleException &e)
350                         {
351                                 return NULL;
352                         }
353
354                         return ret;
355                 }
356                 else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
357                 {
358                         return ServerInstance->Config->DelIOHook((InspSocket*)ISR->Sock) ? (char*)"OK" : NULL;
359                 }
360                 else if (strcmp("IS_HSDONE", request->GetId()) == 0)
361                 {
362                         if (ISR->Sock->GetFd() < 0)
363                                 return (char*)"OK";
364
365                         issl_session* session = &sessions[ISR->Sock->GetFd()];
366                         return (session->status == ISSL_HANDSHAKING) ? NULL : (char*)"OK";
367                 }
368                 else if (strcmp("IS_ATTACH", request->GetId()) == 0)
369                 {
370                         issl_session* session = &sessions[ISR->Sock->GetFd()];
371                         if (session->sess)
372                         {
373                                 VerifyCertificate(session, (InspSocket*)ISR->Sock);
374                                 return "OK";
375                         }
376                 }
377                 return NULL;
378         }
379
380
381         virtual void OnRawSocketAccept(int fd, const std::string &ip, int localport)
382         {
383                 issl_session* session = &sessions[fd];
384
385                 session->fd = fd;
386                 session->inbuf = new char[inbufsize];
387                 session->inbufoffset = 0;
388                 session->sess = SSL_new(ctx);
389                 session->status = ISSL_NONE;
390                 session->outbound = false;
391
392                 if (session->sess == NULL)
393                         return;
394
395                 if (SSL_set_fd(session->sess, fd) == 0)
396                 {
397                         ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
398                         return;
399                 }
400
401                 Handshake(session);
402         }
403
404         virtual void OnRawSocketConnect(int fd)
405         {
406                 issl_session* session = &sessions[fd];
407
408                 session->fd = fd;
409                 session->inbuf = new char[inbufsize];
410                 session->inbufoffset = 0;
411                 session->sess = SSL_new(clictx);
412                 session->status = ISSL_NONE;
413                 session->outbound = true;
414
415                 if (session->sess == NULL)
416                         return;
417
418                 if (SSL_set_fd(session->sess, fd) == 0)
419                 {
420                         ServerInstance->Log(DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
421                         return;
422                 }
423
424                 Handshake(session);
425         }
426
427         virtual void OnRawSocketClose(int fd)
428         {
429                 CloseSession(&sessions[fd]);
430
431                 EventHandler* user = ServerInstance->SE->GetRef(fd);
432
433                 if ((user) && (user->GetExt("ssl_cert", dummy)))
434                 {
435                         ssl_cert* tofree;
436                         user->GetExt("ssl_cert", tofree);
437                         delete tofree;
438                         user->Shrink("ssl_cert");
439                 }
440         }
441
442         virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
443         {
444                 issl_session* session = &sessions[fd];
445
446                 if (!session->sess)
447                 {
448                         readresult = 0;
449                         CloseSession(session);
450                         return 1;
451                 }
452
453                 if (session->status == ISSL_HANDSHAKING)
454                 {
455                         if (session->rstat == ISSL_READ || session->wstat == ISSL_READ)
456                         {
457                                 // The handshake isn't finished and it wants to read, try to finish it.
458                                 if (!Handshake(session))
459                                 {
460                                         // Couldn't resume handshake.
461                                         return -1;
462                                 }
463                         }
464                         else
465                         {
466                                 return -1;
467                         }
468                 }
469
470                 // If we resumed the handshake then session->status will be ISSL_OPEN
471
472                 if (session->status == ISSL_OPEN)
473                 {
474                         if (session->wstat == ISSL_READ)
475                         {
476                                 if(DoWrite(session) == 0)
477                                         return 0;
478                         }
479
480                         if (session->rstat == ISSL_READ)
481                         {
482                                 int ret = DoRead(session);
483
484                                 if (ret > 0)
485                                 {
486                                         if (count <= session->inbufoffset)
487                                         {
488                                                 memcpy(buffer, session->inbuf, count);
489                                                 // Move the stuff left in inbuf to the beginning of it
490                                                 memcpy(session->inbuf, session->inbuf + count, (session->inbufoffset - count));
491                                                 // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
492                                                 session->inbufoffset -= count;
493                                                 // Insp uses readresult as the count of how much data there is in buffer, so:
494                                                 readresult = count;
495                                         }
496                                         else
497                                         {
498                                                 // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
499                                                 memcpy(buffer, session->inbuf, session->inbufoffset);
500
501                                                 readresult = session->inbufoffset;
502                                                 // Zero the offset, as there's nothing there..
503                                                 session->inbufoffset = 0;
504                                         }
505
506                                         return 1;
507                                 }
508                                 else
509                                 {
510                                         return ret;
511                                 }
512                         }
513                 }
514
515                 return -1;
516         }
517
518         virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
519         {
520                 issl_session* session = &sessions[fd];
521
522                 if (!session->sess)
523                 {
524                         CloseSession(session);
525                         return -1;
526                 }
527
528                 session->outbuf.append(buffer, count);
529
530                 if (session->status == ISSL_HANDSHAKING)
531                 {
532                         // The handshake isn't finished, try to finish it.
533                         if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE)
534                                 Handshake(session);
535                 }
536
537                 if (session->status == ISSL_OPEN)
538                 {
539                         if (session->rstat == ISSL_WRITE)
540                                 DoRead(session);
541
542                         if (session->wstat == ISSL_WRITE)
543                                 return DoWrite(session);
544                 }
545
546                 return 1;
547         }
548
549         int DoWrite(issl_session* session)
550         {
551                 if (!session->outbuf.size())
552                         return -1;
553
554                 int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size());
555
556                 if (ret == 0)
557                 {
558                         CloseSession(session);
559                         return 0;
560                 }
561                 else if (ret < 0)
562                 {
563                         int err = SSL_get_error(session->sess, ret);
564
565                         if (err == SSL_ERROR_WANT_WRITE)
566                         {
567                                 session->wstat = ISSL_WRITE;
568                                 return -1;
569                         }
570                         else if (err == SSL_ERROR_WANT_READ)
571                         {
572                                 session->wstat = ISSL_READ;
573                                 return -1;
574                         }
575                         else
576                         {
577                                 CloseSession(session);
578                                 return 0;
579                         }
580                 }
581                 else
582                 {
583                         session->outbuf = session->outbuf.substr(ret);
584                         return ret;
585                 }
586         }
587
588         int DoRead(issl_session* session)
589         {
590                 // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
591                 // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
592
593                 int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
594
595                 if (ret == 0)
596                 {
597                         // Client closed connection.
598                         CloseSession(session);
599                         return 0;
600                 }
601                 else if (ret < 0)
602                 {
603                         int err = SSL_get_error(session->sess, ret);
604
605                         if (err == SSL_ERROR_WANT_READ)
606                         {
607                                 session->rstat = ISSL_READ;
608                                 return -1;
609                         }
610                         else if (err == SSL_ERROR_WANT_WRITE)
611                         {
612                                 session->rstat = ISSL_WRITE;
613                                 return -1;
614                         }
615                         else
616                         {
617                                 CloseSession(session);
618                                 return 0;
619                         }
620                 }
621                 else
622                 {
623                         // Read successfully 'ret' bytes into inbuf + inbufoffset
624                         // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
625                         // 'buffer' is 'count' long
626
627                         session->inbufoffset += ret;
628
629                         return ret;
630                 }
631         }
632
633         // :kenny.chatspike.net 320 Om Epy|AFK :is a Secure Connection
634         virtual void OnWhois(userrec* source, userrec* dest)
635         {
636                 if (!clientactive)
637                         return;
638
639                 // Bugfix, only send this numeric for *our* SSL users
640                 if (dest->GetExt("ssl", dummy) || (IS_LOCAL(dest) &&  isin(dest->GetPort(), listenports)))
641                 {
642                         ServerInstance->SendWhoisLine(source, dest, 320, "%s %s :is using a secure connection", source->nick, dest->nick);
643                 }
644         }
645
646         virtual void OnSyncUserMetaData(userrec* user, Module* proto, void* opaque, const std::string &extname, bool displayable)
647         {
648                 // check if the linking module wants to know about OUR metadata
649                 if (extname == "ssl")
650                 {
651                         // check if this user has an swhois field to send
652                         if(user->GetExt(extname, dummy))
653                         {
654                                 // call this function in the linking module, let it format the data how it
655                                 // sees fit, and send it on its way. We dont need or want to know how.
656                                 proto->ProtoSendMetaData(opaque, TYPE_USER, user, extname, displayable ? "Enabled" : "ON");
657                         }
658                 }
659         }
660
661         virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
662         {
663                 // check if its our metadata key, and its associated with a user
664                 if ((target_type == TYPE_USER) && (extname == "ssl"))
665                 {
666                         userrec* dest = (userrec*)target;
667                         // if they dont already have an ssl flag, accept the remote server's
668                         if (!dest->GetExt(extname, dummy))
669                         {
670                                 dest->Extend(extname, "ON");
671                         }
672                 }
673         }
674
675         bool Handshake(issl_session* session)
676         {
677                 int ret;
678
679                 if (session->outbound)
680                         ret = SSL_connect(session->sess);
681                 else
682                         ret = SSL_accept(session->sess);
683
684                 if (ret < 0)
685                 {
686                         int err = SSL_get_error(session->sess, ret);
687
688                         if (err == SSL_ERROR_WANT_READ)
689                         {
690                                 session->rstat = ISSL_READ;
691                                 session->status = ISSL_HANDSHAKING;
692                         }
693                         else if (err == SSL_ERROR_WANT_WRITE)
694                         {
695                                 session->wstat = ISSL_WRITE;
696                                 session->status = ISSL_HANDSHAKING;
697                                 MakePollWrite(session);
698                         }
699                         else
700                         {
701                                 CloseSession(session);
702                         }
703
704                         return false;
705                 }
706                 else if (ret > 0)
707                 {
708                         // Handshake complete.
709                         // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
710                         userrec* u = ServerInstance->FindDescriptor(session->fd);
711                         if (u)
712                         {
713                                 if (!u->GetExt("ssl", dummy))
714                                         u->Extend("ssl", "ON");
715                         }
716
717                         session->status = ISSL_OPEN;
718
719                         MakePollWrite(session);
720
721                         return true;
722                 }
723                 else if (ret == 0)
724                 {
725                         CloseSession(session);
726                         return true;
727                 }
728
729                 return true;
730         }
731
732         virtual void OnPostConnect(userrec* user)
733         {
734                 // This occurs AFTER OnUserConnect so we can be sure the
735                 // protocol module has propogated the NICK message.
736                 if ((user->GetExt("ssl", dummy)) && (IS_LOCAL(user)))
737                 {
738                         // Tell whatever protocol module we're using that we need to inform other servers of this metadata NOW.
739                         std::deque<std::string>* metadata = new std::deque<std::string>;
740                         metadata->push_back(user->nick);
741                         metadata->push_back("ssl");             // The metadata id
742                         metadata->push_back("ON");              // The value to send
743                         Event* event = new Event((char*)metadata,(Module*)this,"send_metadata");
744                         event->Send(ServerInstance);            // Trigger the event. We don't care what module picks it up.
745                         DELETE(event);
746                         DELETE(metadata);
747
748                         VerifyCertificate(&sessions[user->GetFd()], user);
749                         if (sessions[user->GetFd()].sess)
750                                 user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick, SSL_get_cipher(sessions[user->GetFd()].sess));
751                 }
752         }
753
754         void MakePollWrite(issl_session* session)
755         {
756                 OnRawSocketWrite(session->fd, NULL, 0);
757         }
758
759         void CloseSession(issl_session* session)
760         {
761                 if (session->sess)
762                 {
763                         SSL_shutdown(session->sess);
764                         SSL_free(session->sess);
765                 }
766
767                 if (session->inbuf)
768                 {
769                         delete[] session->inbuf;
770                 }
771
772                 session->outbuf.clear();
773                 session->inbuf = NULL;
774                 session->sess = NULL;
775                 session->status = ISSL_NONE;
776         }
777
778         void VerifyCertificate(issl_session* session, Extensible* user)
779         {
780                 if (!session->sess || !user)
781                         return;
782
783                 X509* cert;
784                 ssl_cert* certinfo = new ssl_cert;
785                 unsigned int n;
786                 unsigned char md[EVP_MAX_MD_SIZE];
787                 const EVP_MD *digest = EVP_md5();
788
789                 user->Extend("ssl_cert",certinfo);
790
791                 cert = SSL_get_peer_certificate((SSL*)session->sess);
792
793                 if (!cert)
794                 {
795                         certinfo->data.insert(std::make_pair("error","Could not get peer certificate: "+std::string(get_error())));
796                         return;
797                 }
798
799                 certinfo->data.insert(std::make_pair("invalid", SSL_get_verify_result(session->sess) != X509_V_OK ? ConvToStr(1) : ConvToStr(0)));
800
801                 if (SelfSigned)
802                 {
803                         certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0)));
804                         certinfo->data.insert(std::make_pair("trusted",ConvToStr(1)));
805                 }
806                 else
807                 {
808                         certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1)));
809                         certinfo->data.insert(std::make_pair("trusted",ConvToStr(0)));
810                 }
811
812                 certinfo->data.insert(std::make_pair("dn",std::string(X509_NAME_oneline(X509_get_subject_name(cert),0,0))));
813                 certinfo->data.insert(std::make_pair("issuer",std::string(X509_NAME_oneline(X509_get_issuer_name(cert),0,0))));
814
815                 if (!X509_digest(cert, digest, md, &n))
816                 {
817                         certinfo->data.insert(std::make_pair("error","Out of memory generating fingerprint"));
818                 }
819                 else
820                 {
821                         certinfo->data.insert(std::make_pair("fingerprint",irc::hex(md, n)));
822                 }
823
824                 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))
825                 {
826                         certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate"));
827                 }
828
829                 X509_free(cert);
830         }
831 };
832
833 static int error_callback(const char *str, size_t len, void *u)
834 {
835         ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u;
836         mssl->PublicInstance->Log(DEFAULT, "SSL error: " + std::string(str, len - 1));
837         return 0;
838 }
839
840 class ModuleSSLOpenSSLFactory : public ModuleFactory
841 {
842  public:
843         ModuleSSLOpenSSLFactory()
844         {
845         }
846
847         ~ModuleSSLOpenSSLFactory()
848         {
849         }
850
851         virtual Module * CreateModule(InspIRCd* Me)
852         {
853                 return new ModuleSSLOpenSSL(Me);
854         }
855 };
856
857
858 extern "C" void * init_module( void )
859 {
860         return new ModuleSSLOpenSSLFactory;
861 }