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