]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
645add4c28a4be963a59d49811f5276caf5846bb
[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
40 static bool SelfSigned = false;
41
42 char* get_error()
43 {
44         return ERR_error_string(ERR_get_error(), NULL);
45 }
46
47 static int error_callback(const char *str, size_t len, void *u);
48
49 /** Represents an SSL user's extra data
50  */
51 class issl_session : public classbase
52 {
53 public:
54         SSL* sess;
55         issl_status status;
56
57         int fd;
58         bool outbound;
59         bool data_to_write;
60
61         issl_session()
62         {
63                 outbound = false;
64                 data_to_write = false;
65         }
66 };
67
68 static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
69 {
70         /* XXX: This will allow self signed certificates.
71          * In the future if we want an option to not allow this,
72          * we can just return preverify_ok here, and openssl
73          * will boot off self-signed and invalid peer certs.
74          */
75         int ve = X509_STORE_CTX_get_error(ctx);
76
77         SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
78
79         return 1;
80 }
81
82 class ModuleSSLOpenSSL : public Module
83 {
84         std::set<ListenSocketBase*> listenports;
85
86         int inbufsize;
87         issl_session* sessions;
88
89         SSL_CTX* ctx;
90         SSL_CTX* clictx;
91
92         char cipher[MAXBUF];
93
94         std::string keyfile;
95         std::string certfile;
96         std::string cafile;
97         // std::string crlfile;
98         std::string dhfile;
99         std::string sslports;
100
101  public:
102
103         ModuleSSLOpenSSL(InspIRCd* Me)
104         {
105                 ServerInstance->Modules->PublishInterface("BufferedSocketHook", this);
106
107                 sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
108
109                 // Not rehashable...because I cba to reduce all the sizes of existing buffers.
110                 inbufsize = ServerInstance->Config->NetBufferSize;
111
112                 /* Global SSL library initialization*/
113                 SSL_library_init();
114                 SSL_load_error_strings();
115
116                 /* Build our SSL contexts:
117                  * NOTE: OpenSSL makes us have two contexts, one for servers and one for clients. ICK.
118                  */
119                 ctx = SSL_CTX_new( SSLv23_server_method() );
120                 clictx = SSL_CTX_new( SSLv23_client_method() );
121
122                 SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
123                 SSL_CTX_set_mode(clictx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
124
125                 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
126                 SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
127
128                 // Needs the flag as it ignores a plain /rehash
129                 OnModuleRehash(NULL,"ssl");
130                 Implementation eventlist[] = {
131                         I_On005Numeric, I_OnRequest, I_OnRehash, I_OnModuleRehash, I_OnPostConnect,
132                         I_OnHookIO };
133                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
134         }
135
136         void OnHookIO(StreamSocket* user, ListenSocketBase* lsb)
137         {
138                 if (!user->GetIOHook() && listenports.find(lsb) != listenports.end())
139                 {
140                         /* Hook the user with our module */
141                         user->AddIOHook(this);
142                 }
143         }
144
145         void OnRehash(User* user)
146         {
147                 ConfigReader Conf(ServerInstance);
148
149                 listenports.clear();
150                 sslports.clear();
151
152                 for (size_t i = 0; i < ServerInstance->ports.size(); i++)
153                 {
154                         ListenSocketBase* port = ServerInstance->ports[i];
155                         std::string desc = port->GetDescription();
156                         if (desc != "openssl")
157                                 continue;
158
159                         listenports.insert(port);
160                         std::string portid = port->GetBindDesc();
161
162                         ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
163                         if (port->GetIP() != "127.0.0.1")
164                                 sslports.append(portid).append(";");
165                 }
166
167                 if (!sslports.empty())
168                         sslports.erase(sslports.end() - 1);
169         }
170
171         void OnModuleRehash(User* user, const std::string &param)
172         {
173                 if (param != "ssl")
174                         return;
175
176                 OnRehash(user);
177
178                 ConfigReader Conf(ServerInstance);
179
180                 std::string confdir(ServerInstance->ConfigFileName);
181                 // +1 so we the path ends with a /
182                 confdir = confdir.substr(0, confdir.find_last_of('/') + 1);
183
184                 cafile   = Conf.ReadValue("openssl", "cafile", 0);
185                 certfile = Conf.ReadValue("openssl", "certfile", 0);
186                 keyfile  = Conf.ReadValue("openssl", "keyfile", 0);
187                 dhfile   = Conf.ReadValue("openssl", "dhfile", 0);
188
189                 // Set all the default values needed.
190                 if (cafile.empty())
191                         cafile = "ca.pem";
192
193                 if (certfile.empty())
194                         certfile = "cert.pem";
195
196                 if (keyfile.empty())
197                         keyfile = "key.pem";
198
199                 if (dhfile.empty())
200                         dhfile = "dhparams.pem";
201
202                 // Prepend relative paths with the path to the config directory.
203                 if ((cafile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(cafile)))
204                         cafile = confdir + cafile;
205
206                 if ((certfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(certfile)))
207                         certfile = confdir + certfile;
208
209                 if ((keyfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(keyfile)))
210                         keyfile = confdir + keyfile;
211
212                 if ((dhfile[0] != '/') && (!ServerInstance->Config->StartsWithWindowsDriveLetter(dhfile)))
213                         dhfile = confdir + dhfile;
214
215                 /* Load our keys and certificates
216                  * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
217                  */
218                 if ((!SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str())) || (!SSL_CTX_use_certificate_chain_file(clictx, certfile.c_str())))
219                 {
220                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read certificate file %s. %s", certfile.c_str(), strerror(errno));
221                         ERR_print_errors_cb(error_callback, this);
222                 }
223
224                 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)))
225                 {
226                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read key file %s. %s", keyfile.c_str(), strerror(errno));
227                         ERR_print_errors_cb(error_callback, this);
228                 }
229
230                 /* Load the CAs we trust*/
231                 if (((!SSL_CTX_load_verify_locations(ctx, cafile.c_str(), 0))) || (!SSL_CTX_load_verify_locations(clictx, cafile.c_str(), 0)))
232                 {
233                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't read CA list from %s. %s", cafile.c_str(), strerror(errno));
234                         ERR_print_errors_cb(error_callback, this);
235                 }
236
237                 FILE* dhpfile = fopen(dhfile.c_str(), "r");
238                 DH* ret;
239
240                 if (dhpfile == NULL)
241                 {
242                         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so Couldn't open DH file %s: %s", dhfile.c_str(), strerror(errno));
243                         throw ModuleException("Couldn't open DH file " + dhfile + ": " + strerror(errno));
244                 }
245                 else
246                 {
247                         ret = PEM_read_DHparams(dhpfile, NULL, NULL, NULL);
248                         if ((SSL_CTX_set_tmp_dh(ctx, ret) < 0) || (SSL_CTX_set_tmp_dh(clictx, ret) < 0))
249                         {
250                                 ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Couldn't set DH parameters %s. SSL errors follow:", dhfile.c_str());
251                                 ERR_print_errors_cb(error_callback, this);
252                         }
253                 }
254
255                 fclose(dhpfile);
256         }
257
258         void On005Numeric(std::string &output)
259         {
260                 if (!sslports.empty())
261                         output.append(" SSL=" + sslports);
262         }
263
264         ~ModuleSSLOpenSSL()
265         {
266                 SSL_CTX_free(ctx);
267                 SSL_CTX_free(clictx);
268                 ServerInstance->Modules->UnpublishInterface("BufferedSocketHook", this);
269                 delete[] sessions;
270         }
271
272         void OnCleanup(int target_type, void* item)
273         {
274                 if (target_type == TYPE_USER)
275                 {
276                         User* user = (User*)item;
277
278                         if (user->GetIOHook() == this)
279                         {
280                                 // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
281                                 // Potentially there could be multiple SSL modules loaded at once on different ports.
282                                 ServerInstance->Users->QuitUser(user, "SSL module unloading");
283                                 user->DelIOHook();
284                         }
285                 }
286         }
287
288         Version GetVersion()
289         {
290                 return Version("Provides SSL support for clients", VF_VENDOR, API_VERSION);
291         }
292
293
294         const char* OnRequest(Request* request)
295         {
296                 ISHRequest* ISR = (ISHRequest*)request;
297                 if (strcmp("IS_NAME", request->GetId()) == 0)
298                 {
299                         return "openssl";
300                 }
301                 else if (strcmp("IS_HOOK", request->GetId()) == 0)
302                 {
303                         ISR->Sock->AddIOHook(this);
304                         return "OK";
305                 }
306                 else if (strcmp("IS_UNHOOK", request->GetId()) == 0)
307                 {
308                         ISR->Sock->DelIOHook();
309                         return "OK";
310                 }
311                 else if (strcmp("IS_HSDONE", request->GetId()) == 0)
312                 {
313                         if (ISR->Sock->GetFd() < 0)
314                                 return "OK";
315
316                         issl_session* session = &sessions[ISR->Sock->GetFd()];
317                         return (session->status == ISSL_HANDSHAKING) ? NULL : "OK";
318                 }
319                 else if (strcmp("IS_ATTACH", request->GetId()) == 0)
320                 {
321                         issl_session* session = &sessions[ISR->Sock->GetFd()];
322                         if (session->sess)
323                         {
324                                 return "OK";
325                         }
326                 }
327                 else if (strcmp("GET_CERT", request->GetId()) == 0)
328                 {
329                         Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
330                         if (sslinfo)
331                                 return sslinfo->OnRequest(request);
332                 }
333                 return NULL;
334         }
335
336
337         void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
338         {
339                 int fd = user->GetFd();
340
341                 issl_session* session = &sessions[fd];
342
343                 session->fd = fd;
344                 session->sess = SSL_new(ctx);
345                 session->status = ISSL_NONE;
346                 session->outbound = false;
347
348                 if (session->sess == NULL)
349                         return;
350
351                 if (SSL_set_fd(session->sess, fd) == 0)
352                 {
353                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
354                         return;
355                 }
356
357                 Handshake(user, session);
358         }
359
360         void OnStreamSocketConnect(StreamSocket* user)
361         {
362                 int fd = user->GetFd();
363                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
364                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1))
365                         return;
366
367                 issl_session* session = &sessions[fd];
368
369                 session->fd = fd;
370                 session->sess = SSL_new(clictx);
371                 session->status = ISSL_NONE;
372                 session->outbound = true;
373
374                 if (session->sess == NULL)
375                         return;
376
377                 if (SSL_set_fd(session->sess, fd) == 0)
378                 {
379                         ServerInstance->Logs->Log("m_ssl_openssl",DEBUG,"BUG: Can't set fd with SSL_set_fd: %d", fd);
380                         return;
381                 }
382
383                 Handshake(user, session);
384         }
385
386         void OnStreamSocketClose(StreamSocket* user)
387         {
388                 int fd = user->GetFd();
389                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
390                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
391                         return;
392
393                 CloseSession(&sessions[fd]);
394         }
395
396         int OnStreamSocketRead(StreamSocket* user, std::string& recvq)
397         {
398                 int fd = user->GetFd();
399                 /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */
400                 if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1))
401                         return -1;
402
403                 issl_session* session = &sessions[fd];
404
405                 if (!session->sess)
406                 {
407                         CloseSession(session);
408                         return -1;
409                 }
410
411                 if (session->status == ISSL_HANDSHAKING)
412                 {
413                         // The handshake isn't finished and it wants to read, try to finish it.
414                         if (!Handshake(user, session))
415                         {
416                                 // Couldn't resume handshake.
417                                 if (session->status == ISSL_NONE)
418                                         return -1;
419                                 return 0;
420                         }
421                 }
422
423                 // If we resumed the handshake then session->status will be ISSL_OPEN
424
425                 if (session->status == ISSL_OPEN)
426                 {
427                         char* buffer = ServerInstance->GetReadBuffer();
428                         size_t bufsiz = ServerInstance->Config->NetBufferSize;
429                         int ret = SSL_read(session->sess, buffer, bufsiz);
430                         
431                         if (ret > 0)
432                         {
433                                 recvq.append(buffer, ret);
434                                 return 1;
435                         }
436                         else if (ret == 0)
437                         {
438                                 // Client closed connection.
439                                 CloseSession(session);
440                                 return -1;
441                         }
442                         else if (ret < 0)
443                         {
444                                 int err = SSL_get_error(session->sess, ret);
445
446                                 if (err == SSL_ERROR_WANT_READ)
447                                 {
448                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
449                                         return 0;
450                                 }
451                                 else if (err == SSL_ERROR_WANT_WRITE)
452                                 {
453                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
454                                         return 0;
455                                 }
456                                 else
457                                 {
458                                         CloseSession(session);
459                                         return -1;
460                                 }
461                         }
462                 }
463
464                 return 0;
465         }
466
467         int OnStreamSocketWrite(StreamSocket* user, std::string& buffer)
468         {
469                 int fd = user->GetFd();
470
471                 issl_session* session = &sessions[fd];
472
473                 if (!session->sess)
474                 {
475                         CloseSession(session);
476                         return -1;
477                 }
478
479                 session->data_to_write = true;
480
481                 if (session->status == ISSL_HANDSHAKING)
482                 {
483                         if (!Handshake(user, session))
484                         {
485                                 // Couldn't resume handshake.
486                                 if (session->status == ISSL_NONE)
487                                         return -1;
488                                 return 0;
489                         }
490                 }
491
492                 if (session->status == ISSL_OPEN)
493                 {
494                         int ret = SSL_write(session->sess, buffer.data(), buffer.size());
495                         if (ret == (int)buffer.length())
496                         {
497                                 session->data_to_write = false;
498                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
499                                 return 1;
500                         }
501                         else if (ret > 0)
502                         {
503                                 buffer = buffer.substr(ret);
504                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
505                                 return 0;
506                         }
507                         else if (ret == 0)
508                         {
509                                 CloseSession(session);
510                                 return -1;
511                         }
512                         else if (ret < 0)
513                         {
514                                 int err = SSL_get_error(session->sess, ret);
515
516                                 if (err == SSL_ERROR_WANT_WRITE)
517                                 {
518                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
519                                         return 0;
520                                 }
521                                 else if (err == SSL_ERROR_WANT_READ)
522                                 {
523                                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
524                                         return 0;
525                                 }
526                                 else
527                                 {
528                                         CloseSession(session);
529                                         return -1;
530                                 }
531                         }
532                 }
533                 return 0;
534         }
535
536         bool Handshake(EventHandler* user, issl_session* session)
537         {
538                 int ret;
539
540                 if (session->outbound)
541                         ret = SSL_connect(session->sess);
542                 else
543                         ret = SSL_accept(session->sess);
544
545                 if (ret < 0)
546                 {
547                         int err = SSL_get_error(session->sess, ret);
548
549                         if (err == SSL_ERROR_WANT_READ)
550                         {
551                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
552                                 session->status = ISSL_HANDSHAKING;
553                                 return true;
554                         }
555                         else if (err == SSL_ERROR_WANT_WRITE)
556                         {
557                                 ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
558                                 session->status = ISSL_HANDSHAKING;
559                                 return true;
560                         }
561                         else
562                         {
563                                 CloseSession(session);
564                         }
565
566                         return false;
567                 }
568                 else if (ret > 0)
569                 {
570                         // Handshake complete.
571                         VerifyCertificate(session, user);
572
573                         session->status = ISSL_OPEN;
574
575                         ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
576
577                         return true;
578                 }
579                 else if (ret == 0)
580                 {
581                         CloseSession(session);
582                         return true;
583                 }
584
585                 return true;
586         }
587
588         void CloseSession(issl_session* session)
589         {
590                 if (session->sess)
591                 {
592                         SSL_shutdown(session->sess);
593                         SSL_free(session->sess);
594                 }
595
596                 session->sess = NULL;
597                 session->status = ISSL_NONE;
598                 errno = EIO;
599         }
600
601         void VerifyCertificate(issl_session* session, Extensible* user)
602         {
603                 if (!session->sess || !user)
604                         return;
605
606                 Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
607                 if (!sslinfo)
608                         return;
609
610                 X509* cert;
611                 ssl_cert* certinfo = new ssl_cert;
612                 unsigned int n;
613                 unsigned char md[EVP_MAX_MD_SIZE];
614                 const EVP_MD *digest = EVP_md5();
615
616                 cert = SSL_get_peer_certificate((SSL*)session->sess);
617
618                 if (!cert)
619                 {
620                         certinfo->error = "Could not get peer certificate: "+std::string(get_error());
621                         BufferedSocketFingerprintSubmission(user, this, sslinfo, certinfo).Send();
622                         return;
623                 }
624
625                 certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK);
626
627                 if (SelfSigned)
628                 {
629                         certinfo->unknownsigner = false;
630                         certinfo->trusted = true;
631                 }
632                 else
633                 {
634                         certinfo->unknownsigner = true;
635                         certinfo->trusted = false;
636                 }
637
638                 certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0);
639                 certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0);
640
641                 if (!X509_digest(cert, digest, md, &n))
642                 {
643                         certinfo->error = "Out of memory generating fingerprint";
644                 }
645                 else
646                 {
647                         certinfo->fingerprint = irc::hex(md, n);
648                 }
649
650                 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))
651                 {
652                         certinfo->error = "Not activated, or expired certificate";
653                 }
654
655                 X509_free(cert);
656                 BufferedSocketFingerprintSubmission(user, this, sslinfo, certinfo).Send();
657         }
658
659         void Prioritize()
660         {
661                 Module* server = ServerInstance->Modules->Find("m_spanningtree.so");
662                 ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_AFTER, &server);
663         }
664
665 };
666
667 static int error_callback(const char *str, size_t len, void *u)
668 {
669         ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "SSL error: " + std::string(str, len - 1));
670
671         //
672         // XXX: Remove this line, it causes valgrind warnings...
673         //
674         // MD_update(&m, buf, j);
675         //
676         //
677         // ... ONLY JOKING! :-)
678         //
679
680         return 0;
681 }
682
683 MODULE_INIT(ModuleSSLOpenSSL)