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