]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
a8043457bee22b461fd89759a43f65e244d90c34
[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                         if (user->GetExt("ssl_cert", dummy))
297                         {
298                                 ssl_cert* tofree;
299                                 user->GetExt("ssl_cert", tofree);
300                                 delete tofree;
301                                 user->Shrink("ssl_cert");
302                         }
303                 }
304         }
305
306         virtual Version GetVersion()
307         {
308                 return Version("$Id$", VF_VENDOR, API_VERSION);
309         }
310
311
312         virtual const char* OnRequest(Request* request)
313         {
314                 ISHRequest* ISR = (ISHRequest*)request;
315                 if (strcmp("IS_NAME", request->GetId()) == 0)
316                 {
317                         return "openssl";
318                 }
319                 else if (strcmp("IS_HOOK", request->GetId()) == 0)
320                 {
321                         const char* ret = "OK";
322                         try
323                         {
324                                 ret = ISR->Sock->AddIOHook((Module*)this) ? "OK" : NULL;
325                         }
326                         catch (ModuleException &e)
327                         {
328                                 return NULL;
329                         }
330
331                         return ret;
332                 }
333                 else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
334                 {
335                         return ISR->Sock->DelIOHook() ? "OK" : NULL;
336                 }
337                 else if (strcmp("IS_HSDONE", request->GetId()) == 0)
338                 {
339                         if (ISR->Sock->GetFd() < 0)
340                                 return "OK";
341
342                         issl_session* session = &sessions[ISR->Sock->GetFd()];
343                         return (session->status == ISSL_HANDSHAKING) ? NULL : "OK";
344                 }
345                 else if (strcmp("IS_ATTACH", request->GetId()) == 0)
346                 {
347                         issl_session* session = &sessions[ISR->Sock->GetFd()];
348                         if (session->sess)
349                         {
350                                 VerifyCertificate(session, (BufferedSocket*)ISR->Sock);
351                                 return "OK";
352                         }
353                 }
354                 else if (strcmp("GET_FP", request->GetId()) == 0)
355                 {
356                         if (ISR->Sock->GetFd() > -1)
357                         {
358                                 issl_session* session = &sessions[ISR->Sock->GetFd()];
359                                 if (session->sess)
360                                 {
361                                         Extensible* ext = ISR->Sock;
362                                         ssl_cert* certinfo;
363                                         if (ext->GetExt("ssl_cert",certinfo))
364                                                 return certinfo->GetFingerprint().c_str();
365                                 }
366                         }
367                 }
368                 return NULL;
369         }
370
371
372         virtual void OnRawSocketAccept(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
373         {
374                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
375                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
376                         return;
377
378                 issl_session* session = &sessions[fd];
379
380                 session->fd = fd;
381                 session->inbuf = new char[inbufsize];
382                 session->inbufoffset = 0;
383                 session->sess = SSL_new(ctx);
384                 session->status = ISSL_NONE;
385                 session->outbound = false;
386
387                 if (session->sess == NULL)
388                         return;
389
390                 if (SSL_set_fd(session->sess, fd) == 0)
391                 {
392                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
393                         return;
394                 }
395
396                 Handshake(session);
397         }
398
399         virtual void OnRawSocketConnect(int fd)
400         {
401                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
402                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
403                         return;
404
405                 issl_session* session = &sessions[fd];
406
407                 session->fd = fd;
408                 session->inbuf = new char[inbufsize];
409                 session->inbufoffset = 0;
410                 session->sess = SSL_new(clictx);
411                 session->status = ISSL_NONE;
412                 session->outbound = true;
413
414                 if (session->sess == NULL)
415                         return;
416
417                 if (SSL_set_fd(session->sess, fd) == 0)
418                 {
419                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
420                         return;
421                 }
422
423                 Handshake(session);
424         }
425
426         virtual void OnRawSocketClose(int fd)
427         {
428                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
429                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
430                         return;
431
432                 CloseSession(&sessions[fd]);
433
434                 EventHandler* user = ServerInstance->SE->GetRef(fd);
435
436                 if ((user) && (user->GetExt("ssl_cert", dummy)))
437                 {
438                         ssl_cert* tofree;
439                         user->GetExt("ssl_cert", tofree);
440                         delete tofree;
441                         user->Shrink("ssl_cert");
442                 }
443         }
444
445         virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult)
446         {
447                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
448                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
449                         return 0;
450
451                 issl_session* session = &sessions[fd];
452
453                 if (!session->sess)
454                 {
455                         readresult = 0;
456                         CloseSession(session);
457                         return 1;
458                 }
459
460                 if (session->status == ISSL_HANDSHAKING)
461                 {
462                         if (session->rstat == ISSL_READ || session->wstat == ISSL_READ)
463                         {
464                                 // The handshake isn't finished and it wants to read, try to finish it.
465                                 if (!Handshake(session))
466                                 {
467                                         // Couldn't resume handshake.
468                                         errno = session->status == ISSL_NONE ? EIO : EAGAIN;
469                                         return -1;
470                                 }
471                         }
472                         else
473                         {
474                                 errno = EAGAIN;
475                                 return -1;
476                         }
477                 }
478
479                 // If we resumed the handshake then session->status will be ISSL_OPEN
480
481                 if (session->status == ISSL_OPEN)
482                 {
483                         if (session->wstat == ISSL_READ)
484                         {
485                                 if(DoWrite(session) == 0)
486                                         return 0;
487                         }
488
489                         if (session->rstat == ISSL_READ)
490                         {
491                                 int ret = DoRead(session);
492
493                                 if (ret > 0)
494                                 {
495                                         if (count <= session->inbufoffset)
496                                         {
497                                                 memcpy(buffer, session->inbuf, count);
498                                                 // Move the stuff left in inbuf to the beginning of it
499                                                 memmove(session->inbuf, session->inbuf + count, (session->inbufoffset - count));
500                                                 // Now we need to set session->inbufoffset to the amount of data still waiting to be handed to insp.
501                                                 session->inbufoffset -= count;
502                                                 // Insp uses readresult as the count of how much data there is in buffer, so:
503                                                 readresult = count;
504                                         }
505                                         else
506                                         {
507                                                 // There's not as much in the inbuf as there is space in the buffer, so just copy the whole thing.
508                                                 memcpy(buffer, session->inbuf, session->inbufoffset);
509
510                                                 readresult = session->inbufoffset;
511                                                 // Zero the offset, as there's nothing there..
512                                                 session->inbufoffset = 0;
513                                         }
514                                         return 1;
515                                 }
516                                 return ret;
517                         }
518                 }
519
520                 return -1;
521         }
522
523         virtual int OnRawSocketWrite(int fd, const char* buffer, int count)
524         {
525                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
526                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
527                         return 0;
528
529                 errno = EAGAIN;
530                 issl_session* session = &sessions[fd];
531
532                 if (!session->sess)
533                 {
534                         CloseSession(session);
535                         return -1;
536                 }
537
538                 session->outbuf.append(buffer, count);
539                 MakePollWrite(session);
540
541                 if (session->status == ISSL_HANDSHAKING)
542                 {
543                         // The handshake isn't finished, try to finish it.
544                         if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE)
545                         {
546                                 if (!Handshake(session))
547                                 {
548                                         // Couldn't resume handshake.
549                                         errno = session->status == ISSL_NONE ? EIO : EAGAIN;
550                                         return -1;
551                                 }
552                         }
553                 }
554
555                 if (session->status == ISSL_OPEN)
556                 {
557                         if (session->rstat == ISSL_WRITE)
558                         {
559                                 DoRead(session);
560                         }
561
562                         if (session->wstat == ISSL_WRITE)
563                         {
564                                 return DoWrite(session);
565                         }
566                 }
567
568                 return 1;
569         }
570
571         int DoWrite(issl_session* session)
572         {
573                 if (!session->outbuf.size())
574                         return -1;
575
576                 int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size());
577
578                 if (ret == 0)
579                 {
580                         CloseSession(session);
581                         return 0;
582                 }
583                 else if (ret < 0)
584                 {
585                         int err = SSL_get_error(session->sess, ret);
586
587                         if (err == SSL_ERROR_WANT_WRITE)
588                         {
589                                 session->wstat = ISSL_WRITE;
590                                 return -1;
591                         }
592                         else if (err == SSL_ERROR_WANT_READ)
593                         {
594                                 session->wstat = ISSL_READ;
595                                 return -1;
596                         }
597                         else
598                         {
599                                 CloseSession(session);
600                                 return 0;
601                         }
602                 }
603                 else
604                 {
605                         session->outbuf = session->outbuf.substr(ret);
606                         return ret;
607                 }
608         }
609
610         int DoRead(issl_session* session)
611         {
612                 // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
613                 // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
614
615                 int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
616
617                 if (ret == 0)
618                 {
619                         // Client closed connection.
620                         CloseSession(session);
621                         return 0;
622                 }
623                 else if (ret < 0)
624                 {
625                         int err = SSL_get_error(session->sess, ret);
626
627                         if (err == SSL_ERROR_WANT_READ)
628                         {
629                                 session->rstat = ISSL_READ;
630                                 return -1;
631                         }
632                         else if (err == SSL_ERROR_WANT_WRITE)
633                         {
634                                 session->rstat = ISSL_WRITE;
635                                 MakePollWrite(session);
636                                 return -1;
637                         }
638                         else
639                         {
640                                 CloseSession(session);
641                                 return 0;
642                         }
643                 }
644                 else
645                 {
646                         // Read successfully 'ret' bytes into inbuf + inbufoffset
647                         // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
648                         // 'buffer' is 'count' long
649
650                         session->inbufoffset += ret;
651
652                         return ret;
653                 }
654         }
655
656         bool Handshake(issl_session* session)
657         {
658                 int ret;
659
660                 if (session->outbound)
661                         ret = SSL_connect(session->sess);
662                 else
663                         ret = SSL_accept(session->sess);
664
665                 if (ret < 0)
666                 {
667                         int err = SSL_get_error(session->sess, ret);
668
669                         if (err == SSL_ERROR_WANT_READ)
670                         {
671                                 session->rstat = ISSL_READ;
672                                 session->status = ISSL_HANDSHAKING;
673                                 return true;
674                         }
675                         else if (err == SSL_ERROR_WANT_WRITE)
676                         {
677                                 session->wstat = ISSL_WRITE;
678                                 session->status = ISSL_HANDSHAKING;
679                                 MakePollWrite(session);
680                                 return true;
681                         }
682                         else
683                         {
684                                 CloseSession(session);
685                         }
686
687                         return false;
688                 }
689                 else if (ret > 0)
690                 {
691                         // Handshake complete.
692                         // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
693                         EventHandler *u = ServerInstance->SE->GetRef(session->fd);
694                         if (u)
695                         {
696                                 u->Extend("ssl");
697                         }
698
699                         session->status = ISSL_OPEN;
700
701                         MakePollWrite(session);
702
703                         return true;
704                 }
705                 else if (ret == 0)
706                 {
707                         CloseSession(session);
708                         return true;
709                 }
710
711                 return true;
712         }
713
714         virtual void OnPostConnect(User* user)
715         {
716                 // This occurs AFTER OnUserConnect so we can be sure the
717                 // protocol module has propagated the NICK message.
718                 if ((user->GetIOHook() == this) && (IS_LOCAL(user)))
719                 {
720                         ssl_cert* certdata = VerifyCertificate(&sessions[user->GetFd()], user);
721                         if (sessions[user->GetFd()].sess)
722                                 user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->GetFd()].sess));
723
724                         ServerInstance->PI->SendMetaData(user, "ssl", "ON");
725                         if (certdata)
726                                 ServerInstance->PI->SendMetaData(user, "ssl_cert", certdata->GetMetaLine().c_str());
727                 }
728         }
729
730         void MakePollWrite(issl_session* session)
731         {
732                 //OnRawSocketWrite(session->fd, NULL, 0);
733                 EventHandler* eh = ServerInstance->SE->GetRef(session->fd);
734                 if (eh)
735                 {
736                         ServerInstance->SE->WantWrite(eh);
737                 }
738         }
739
740         virtual void OnBufferFlushed(User* user)
741         {
742                 if (user->GetIOHook() == this)
743                 {
744                         issl_session* session = &sessions[user->GetFd()];
745                         if (session && session->outbuf.size())
746                                 OnRawSocketWrite(user->GetFd(), NULL, 0);
747                 }
748         }
749
750         void CloseSession(issl_session* session)
751         {
752                 if (session->sess)
753                 {
754                         SSL_shutdown(session->sess);
755                         SSL_free(session->sess);
756                 }
757
758                 if (session->inbuf)
759                 {
760                         delete[] session->inbuf;
761                 }
762
763                 session->outbuf.clear();
764                 session->inbuf = NULL;
765                 session->sess = NULL;
766                 session->status = ISSL_NONE;
767                 errno = EIO;
768         }
769
770         ssl_cert* VerifyCertificate(issl_session* session, Extensible* user)
771         {
772                 if (!session->sess || !user)
773                         return NULL;
774
775                 X509* cert;
776                 ssl_cert* certinfo = new ssl_cert;
777                 unsigned int n;
778                 unsigned char md[EVP_MAX_MD_SIZE];
779                 const EVP_MD *digest = EVP_md5();
780
781                 user->Extend("ssl_cert",certinfo);
782
783                 cert = SSL_get_peer_certificate((SSL*)session->sess);
784
785                 if (!cert)
786                 {
787                         certinfo->error = "Could not get peer certificate: "+std::string(get_error());
788                         return certinfo;
789                 }
790
791                 certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
792
793                 if (SelfSigned)
794                 {
795                         certinfo->unknownsigner = false;
796                         certinfo->trusted = true;
797                 }
798                 else
799                 {
800                         certinfo->unknownsigner = true;
801                         certinfo->trusted = false;
802                 }
803
804                 certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
805                 certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
806
807                 if (!X509_digest(cert, digest, md, &n))
808                 {
809                         certinfo->error = "Out of memory generating fingerprint";
810                 }
811                 else
812                 {
813                         certinfo->fingerprint = irc::hex(md, n);
814                 }
815
816                 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))
817                 {
818                         certinfo->error = "Not activated, or expired certificate";
819                 }
820
821                 X509_free(cert);
822                 return certinfo;
823         }
824
825         void Prioritize()
826         {
827                 Module* server = ServerInstance->Modules->Find("m_spanningtree.so");
828                 ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_AFTER, &server);
829         }
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->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
837
838         //
839         // XXX: Remove this line, it causes valgrind warnings...
840         //
841         // MD_update(&m, buf, j);
842         //
843         //
844         // ... ONLY JOKING! :-)
845         //
846
847         return 0;
848 }
849
850 MODULE_INIT(ModuleSSLOpenSSL)