]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
Create StreamSocket for IO hooking implementation
[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;
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 cipher[MAXBUF];
99
100         std::string keyfile;
101         std::string certfile;
102         std::string cafile;
103         // std::string crlfile;
104         std::string dhfile;
105         std::string sslports;
106
107  public:
108
109         InspIRCd* PublicInstance;
110
111         ModuleSSLOpenSSL(InspIRCd* Me)
112         : Module(Me), PublicInstance(Me)
113         {
114                 ServerInstance->Modules->PublishInterface("BufferedSocketHook", this);
115
116                 sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
117
118                 // Not rehashable...because I cba to reduce all the sizes of existing buffers.
119                 inbufsize = ServerInstance->Config->NetBufferSize;
120
121                 /* Global SSL library initialization*/
122                 SSL_library_init();
123                 SSL_load_error_strings();
124
125                 /* Build our SSL contexts:
126                  * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
127                  */
128                 ctx = SSL_CTX_new( SSLv23_server_method() );
129                 clictx = SSL_CTX_new( SSLv23_client_method() );
130
131                 SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
132                 SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
133
134                 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
135                 SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
136
137                 // Needs the flag as it ignores a plain /rehash
138                 OnModuleRehash(NULL,"ssl");
139                 Implementation eventlist[] = {
140                         I_On005Numeric, I_OnBufferFlushed, I_OnRequest, I_OnRehash, I_OnModuleRehash, I_OnPostConnect,
141                         I_OnHookIO };
142                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
143         }
144
145         void OnHookIO(StreamSocket* user, ListenSocketBase* lsb)
146         {
147                 if (!user->GetIOHook() && listenports.find(lsb) != listenports.end())
148                 {
149                         /* Hook the user with our module */
150                         user->AddIOHook(this);
151                 }
152         }
153
154         void OnRehash(User* user)
155         {
156                 ConfigReader Conf(ServerInstance);
157
158                 listenports.clear();
159                 sslports.clear();
160
161                 for (size_t i = 0; i < ServerInstance->ports.size(); i++)
162                 {
163                         ListenSocketBase* port = ServerInstance->ports[i];
164                         std::string desc = port->GetDescription();
165                         if (desc != "openssl")
166                                 continue;
167
168                         listenports.insert(port);
169                         std::string portid = port->GetBindDesc();
170
171                         ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
172                         if (port->GetIP() != "127.0.0.1")
173                                 sslports.append(portid).append(";");
174                 }
175
176                 if (!sslports.empty())
177                         sslports.erase(sslports.end() - 1);
178         }
179
180         void OnModuleRehash(User* user, const std::string &param)
181         {
182                 if (param != "ssl")
183                         return;
184
185                 OnRehash(user);
186
187                 ConfigReader Conf(ServerInstance);
188
189                 std::string confdir(ServerInstance->ConfigFileName);
190                 // +1 so we the path ends with a /
191                 confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
192
193                 cafile   = Conf.ReadValue("openssl", "cafile", 0);
194                 certfile = Conf.ReadValue("openssl", "certfile", 0);
195                 keyfile  = Conf.ReadValue("openssl", "keyfile", 0);
196                 dhfile   = Conf.ReadValue("openssl", "dhfile", 0);
197
198                 // Set all the default values needed.
199                 if (cafile.empty())
200                         cafile = "ca.pem";
201
202                 if (certfile.empty())
203                         certfile = "cert.pem";
204
205                 if (keyfile.empty())
206                         keyfile = "key.pem";
207
208                 if (dhfile.empty())
209                         dhfile = "dhparams.pem";
210
211                 // Prepend relative paths with the path to the config directory.
212                 if ((cafile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(cafile)))
213                         cafile = confdir + cafile;
214
215                 if ((certfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(certfile)))
216                         certfile = confdir + certfile;
217
218                 if ((keyfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(keyfile)))
219                         keyfile = confdir + keyfile;
220
221                 if ((dhfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(dhfile)))
222                         dhfile = confdir + dhfile;
223
224                 /* Load our keys and certificates
225                  * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
226                  */
227                 if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
228                 {
229                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
230                         ERR_print_errors_cb(error_callback, this);
231                 }
232
233                 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)))
234                 {
235                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
236                         ERR_print_errors_cb(error_callback, this);
237                 }
238
239                 /* Load the CAs we trust*/
240                 if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
241                 {
242                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno));
243                         ERR_print_errors_cb(error_callback, this);
244                 }
245
246                 FILE* dhpfile = fopen(dhfile.c_str(), "r");
247                 DH* ret;
248
249                 if (dhpfile == NULL)
250                 {
251                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
252                         throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
253                 }
254                 else
255                 {
256                         ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
257                         if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
258                         {
259                                 ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
260                                 ERR_print_errors_cb(error_callback, this);
261                         }
262                 }
263
264                 fclose(dhpfile);
265         }
266
267         void On005Numeric(std::string &output)
268         {
269                 if (!sslports.empty())
270                         output.append(" SSL=" + sslports);
271         }
272
273         ~ModuleSSLOpenSSL()
274         {
275                 SSL_CTX_free(ctx);
276                 SSL_CTX_free(clictx);
277                 ServerInstance->Modules->UnpublishInterface("BufferedSocketHook", this);
278                 delete[] sessions;
279         }
280
281         void OnCleanup(int target_type, void* item)
282         {
283                 if (target_type == TYPE_USER)
284                 {
285                         User* user = (User*)item;
286
287                         if (user->GetIOHook() == this)
288                         {
289                                 // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
290                                 // Potentially there could be multiple SSL modules loaded at once on different ports.
291                                 ServerInstance->Users->QuitUser(user, "SSL module unloading");
292                                 user->DelIOHook();
293                         }
294                 }
295         }
296
297         Version GetVersion()
298         {
299                 return Version("$Id$", VF_VENDOR, API_VERSION);
300         }
301
302
303         const char* OnRequest(Request* request)
304         {
305                 ISHRequest* ISR = (ISHRequest*)request;
306                 if (strcmp("IS_NAME", request->GetId()) == 0)
307                 {
308                         return "openssl";
309                 }
310                 else if (strcmp("IS_HOOK", request->GetId()) == 0)
311                 {
312                         ISR->Sock->AddIOHook(this);
313                         return "OK";
314                 }
315                 else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
316                 {
317                         ISR->Sock->DelIOHook();
318                         return "OK";
319                 }
320                 else if (strcmp("IS_HSDONE", request->GetId()) == 0)
321                 {
322                         if (ISR->Sock->GetFd() < 0)
323                                 return "OK";
324
325                         issl_session* session = &sessions[ISR->Sock->GetFd()];
326                         return (session->status == ISSL_HANDSHAKING) ? NULL : "OK";
327                 }
328                 else if (strcmp("IS_ATTACH", request->GetId()) == 0)
329                 {
330                         issl_session* session = &sessions[ISR->Sock->GetFd()];
331                         if (session->sess)
332                         {
333                                 return "OK";
334                         }
335                 }
336                 else if (strcmp("GET_CERT", request->GetId()) == 0)
337                 {
338                         Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
339                         if (sslinfo)
340                                 return sslinfo->OnRequest(request);
341                 }
342                 return NULL;
343         }
344
345
346         void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
347         {
348                 int fd = user->GetFd();
349
350                 issl_session* session = &sessions[fd];
351
352                 session->fd = fd;
353                 session->inbuf = new char[inbufsize];
354                 session->inbufoffset = 0;
355                 session->sess = SSL_new(ctx);
356                 session->status = ISSL_NONE;
357                 session->outbound = false;
358
359                 if (session->sess == NULL)
360                         return;
361
362                 if (SSL_set_fd(session->sess, fd) == 0)
363                 {
364                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
365                         return;
366                 }
367
368                 Handshake(user, session);
369         }
370
371         void OnStreamSocketConnect(StreamSocket* user)
372         {
373                 int fd = user->GetFd();
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(clictx);
384                 session->status = ISSL_NONE;
385                 session->outbound = true;
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(user, session);
397         }
398
399         void OnStreamSocketClose(StreamSocket* user)
400         {
401                 int fd = user->GetFd();
402                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
403                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
404                         return;
405
406                 CloseSession(&sessions[fd]);
407         }
408
409         int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
410         {
411                 int fd = user->GetFd();
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 -1;
415
416                 issl_session* session = &sessions[fd];
417
418                 if (!session->sess)
419                 {
420                         CloseSession(session);
421                         return -1;
422                 }
423
424                 if (session->status == ISSL_HANDSHAKING)
425                 {
426                         if (session->rstat == ISSL_READ || session->wstat == ISSL_READ)
427                         {
428                                 // The handshake isn't finished and it wants to read, try to finish it.
429                                 if (!Handshake(user, session))
430                                 {
431                                         // Couldn't resume handshake.
432                                         if (session->status == ISSL_NONE)
433                                                 return -1;
434                                         return 0;
435                                 }
436                         }
437                         else
438                         {
439                                 return 0;
440                         }
441                 }
442
443                 // If we resumed the handshake then session->status will be ISSL_OPEN
444
445                 if (session->status == ISSL_OPEN)
446                 {
447                         if (session->wstat == ISSL_READ)
448                         {
449                                 if(DoWrite(user, session) == 0)
450                                         return 0;
451                         }
452
453                         if (session->rstat == ISSL_READ)
454                         {
455                                 int ret = DoRead(user, session);
456
457                                 if (ret > 0)
458                                 {
459                                         recvq.append(session->inbuf, session->inbufoffset);
460                                         session->inbufoffset = 0;
461                                         return 1;
462                                 }
463                                 else if (errno == EAGAIN || errno == EINTR)
464                                         return 0;
465                                 else
466                                         return -1;
467                         }
468                 }
469
470                 return 0;
471         }
472
473         int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
474         {
475                 int fd = user->GetFd();
476                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
477                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
478                         return -1;
479
480                 issl_session* session = &sessions[fd];
481
482                 if (!session->sess)
483                 {
484                         CloseSession(session);
485                         return -1;
486                 }
487
488                 if (session->status == ISSL_HANDSHAKING)
489                 {
490                         // The handshake isn't finished, try to finish it.
491                         if (session->rstat == ISSL_WRITE || session->wstat == ISSL_WRITE)
492                         {
493                                 if (!Handshake(user, session))
494                                 {
495                                         // Couldn't resume handshake.
496                                         if (session->status == ISSL_NONE)
497                                                 return -1;
498                                         return 0;
499                                 }
500                         }
501                 }
502
503                 int rv = 0;
504
505                 // don't pull items into the output buffer until they are
506                 // unlikely to block; this allows sendq exceeded to continue
507                 // to work for SSL users.
508                 // TODO better signaling for I/O requests so this isn't needed
509                 if (session->outbuf.empty())
510                 {
511                         session->outbuf = buffer;
512                         rv = 1;
513                 }
514
515                 if (session->status == ISSL_OPEN)
516                 {
517                         if (session->rstat == ISSL_WRITE)
518                         {
519                                 DoRead(user, session);
520                         }
521
522                         if (session->wstat == ISSL_WRITE)
523                         {
524                                 DoWrite(user, session);
525                         }
526                 }
527
528                 if (rv == 0 || !session->outbuf.empty())
529                         ServerInstance->SE->WantWrite(user);
530
531                 return rv;
532         }
533
534         int DoWrite(StreamSocket* user, issl_session* session)
535         {
536                 if (!session->outbuf.size())
537                         return -1;
538
539                 int ret = SSL_write(session->sess, session->outbuf.data(), session->outbuf.size());
540
541                 if (ret == 0)
542                 {
543                         CloseSession(session);
544                         return 0;
545                 }
546                 else if (ret < 0)
547                 {
548                         int err = SSL_get_error(session->sess, ret);
549
550                         if (err == SSL_ERROR_WANT_WRITE)
551                         {
552                                 session->wstat = ISSL_WRITE;
553                                 ServerInstance->SE->WantWrite(user);
554                                 return -1;
555                         }
556                         else if (err == SSL_ERROR_WANT_READ)
557                         {
558                                 session->wstat = ISSL_READ;
559                                 return -1;
560                         }
561                         else
562                         {
563                                 CloseSession(session);
564                                 return 0;
565                         }
566                 }
567                 else
568                 {
569                         session->outbuf = session->outbuf.substr(ret);
570                         return ret;
571                 }
572         }
573
574         int DoRead(StreamSocket* user, issl_session* session)
575         {
576                 // Is this right? Not sure if the unencrypted data is garaunteed to be the same length.
577                 // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet.
578
579                 int ret = SSL_read(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset);
580
581                 if (ret == 0)
582                 {
583                         // Client closed connection.
584                         CloseSession(session);
585                         return 0;
586                 }
587                 else if (ret < 0)
588                 {
589                         int err = SSL_get_error(session->sess, ret);
590
591                         if (err == SSL_ERROR_WANT_READ)
592                         {
593                                 session->rstat = ISSL_READ;
594                                 return -1;
595                         }
596                         else if (err == SSL_ERROR_WANT_WRITE)
597                         {
598                                 session->rstat = ISSL_WRITE;
599                                 ServerInstance->SE->WantWrite(user);
600                                 return -1;
601                         }
602                         else
603                         {
604                                 CloseSession(session);
605                                 return 0;
606                         }
607                 }
608                 else
609                 {
610                         // Read successfully 'ret' bytes into inbuf + inbufoffset
611                         // There are 'ret' + 'inbufoffset' bytes of data in 'inbuf'
612                         // 'buffer' is 'count' long
613
614                         session->inbufoffset += ret;
615
616                         return ret;
617                 }
618         }
619
620         bool Handshake(EventHandler* user, issl_session* session)
621         {
622                 int ret;
623
624                 if (session->outbound)
625                         ret = SSL_connect(session->sess);
626                 else
627                         ret = SSL_accept(session->sess);
628
629                 if (ret < 0)
630                 {
631                         int err = SSL_get_error(session->sess, ret);
632
633                         if (err == SSL_ERROR_WANT_READ)
634                         {
635                                 session->rstat = ISSL_READ;
636                                 session->status = ISSL_HANDSHAKING;
637                                 return true;
638                         }
639                         else if (err == SSL_ERROR_WANT_WRITE)
640                         {
641                                 session->wstat = ISSL_WRITE;
642                                 session->status = ISSL_HANDSHAKING;
643                                 ServerInstance->SE->WantWrite(user);
644                                 return true;
645                         }
646                         else
647                         {
648                                 CloseSession(session);
649                         }
650
651                         return false;
652                 }
653                 else if (ret > 0)
654                 {
655                         // Handshake complete.
656                         // This will do for setting the ssl flag...it could be done earlier if it's needed. But this seems neater.
657                         EventHandler *u = ServerInstance->SE->GetRef(session->fd);
658                         VerifyCertificate(session, u);
659
660                         session->status = ISSL_OPEN;
661
662                         ServerInstance->SE->WantWrite(user);
663
664                         return true;
665                 }
666                 else if (ret == 0)
667                 {
668                         CloseSession(session);
669                         return true;
670                 }
671
672                 return true;
673         }
674
675         void OnBufferFlushed(User* user)
676         {
677                 if (user->GetIOHook() == this)
678                 {
679                         std::string dummy;
680                         issl_session* session = &sessions[user->GetFd()];
681                         if (session && session->outbuf.size())
682                                 OnStreamSocketWrite(user, dummy);
683                 }
684         }
685
686         void CloseSession(issl_session* session)
687         {
688                 if (session->sess)
689                 {
690                         SSL_shutdown(session->sess);
691                         SSL_free(session->sess);
692                 }
693
694                 if (session->inbuf)
695                 {
696                         delete[] session->inbuf;
697                 }
698
699                 session->outbuf.clear();
700                 session->inbuf = NULL;
701                 session->sess = NULL;
702                 session->status = ISSL_NONE;
703                 errno = EIO;
704         }
705
706         void VerifyCertificate(issl_session* session, Extensible* user)
707         {
708                 if (!session->sess || !user)
709                         return;
710
711                 Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
712                 if (!sslinfo)
713                         return;
714
715                 X509* cert;
716                 ssl_cert* certinfo = new ssl_cert;
717                 unsigned int n;
718                 unsigned char md[EVP_MAX_MD_SIZE];
719                 const EVP_MD *digest = EVP_md5();
720
721                 cert = SSL_get_peer_certificate((SSL*)session->sess);
722
723                 if (!cert)
724                 {
725                         certinfo->error = "Could not get peer certificate: "+std::string(get_error());
726                         BufferedSocketFingerprintSubmission(user, this, sslinfo, certinfo).Send();
727                         return;
728                 }
729
730                 certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
731
732                 if (SelfSigned)
733                 {
734                         certinfo->unknownsigner = false;
735                         certinfo->trusted = true;
736                 }
737                 else
738                 {
739                         certinfo->unknownsigner = true;
740                         certinfo->trusted = false;
741                 }
742
743                 certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
744                 certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
745
746                 if (!X509_digest(cert, digest, md, &n))
747                 {
748                         certinfo->error = "Out of memory generating fingerprint";
749                 }
750                 else
751                 {
752                         certinfo->fingerprint = irc::hex(md, n);
753                 }
754
755                 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))
756                 {
757                         certinfo->error = "Not activated, or expired certificate";
758                 }
759
760                 X509_free(cert);
761                 BufferedSocketFingerprintSubmission(user, this, sslinfo, certinfo).Send();
762         }
763
764         void Prioritize()
765         {
766                 Module* server = ServerInstance->Modules->Find("m_spanningtree.so");
767                 ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_AFTER, &server);
768         }
769
770 };
771
772 static int error_callback(const char *str, size_t len, void *u)
773 {
774         ModuleSSLOpenSSL* mssl = (ModuleSSLOpenSSL*)u;
775         mssl->PublicInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
776
777         //
778         // XXX: Remove this line, it causes valgrind warnings...
779         //
780         // MD_update(&m, buf, j);
781         //
782         //
783         // ... ONLY JOKING! :-)
784         //
785
786         return 0;
787 }
788
789 MODULE_INIT(ModuleSSLOpenSSL)