]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_ssl_openssl.cpp
Remove usage of deprecated API members in m_ssl_openssl.
[user/henk/code/inspircd.git] / src / modules / extra / m_ssl_openssl.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
6  *   Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
7  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
8  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
9  *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 /// $CompilerFlags: find_compiler_flags("openssl")
25 /// $LinkerFlags: find_linker_flags("openssl" "-lssl -lcrypto")
26
27 /// $PackageInfo: require_system("centos") openssl-devel pkgconfig
28 /// $PackageInfo: require_system("darwin") openssl pkg-config
29 /// $PackageInfo: require_system("debian") libssl-dev openssl pkg-config
30 /// $PackageInfo: require_system("ubuntu") libssl-dev openssl pkg-config
31
32
33 #include "inspircd.h"
34 #include "iohook.h"
35 #include "modules/ssl.h"
36
37 // Ignore OpenSSL deprecation warnings on OS X Lion and newer.
38 #if defined __APPLE__
39 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
40 #endif
41
42 // Fix warnings about the use of `long long` on C++03.
43 #if defined __clang__
44 # pragma clang diagnostic ignored "-Wc++11-long-long"
45 #elif defined __GNUC__
46 # pragma GCC diagnostic ignored "-Wlong-long"
47 #endif
48
49 #include <openssl/ssl.h>
50 #include <openssl/err.h>
51 #include <openssl/dh.h>
52
53 #ifdef _WIN32
54 # pragma comment(lib, "ssleay32.lib")
55 # pragma comment(lib, "libeay32.lib")
56 #endif
57
58 // Compatibility layer to allow OpenSSL 1.0 to use the 1.1 API.
59 #if ((defined LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L))
60
61 // BIO is opaque in OpenSSL 1.1 but the access API does not exist in 1.0.
62 # define BIO_get_data(BIO) BIO->ptr
63 # define BIO_set_data(BIO, VALUE) BIO->ptr = VALUE;
64 # define BIO_set_init(BIO, VALUE) BIO->init = VALUE;
65
66 // These functions have been renamed in OpenSSL 1.1.
67 # define OpenSSL_version SSLeay_version
68 # define X509_getm_notAfter X509_get_notAfter
69 # define X509_getm_notBefore X509_get_notBefore
70 # define OPENSSL_init_ssl(OPTIONS, SETTINGS) \
71         SSL_library_init(); \
72         SSL_load_error_strings();
73
74 // These macros have been renamed in OpenSSL 1.1.
75 # define OPENSSL_VERSION SSLEAY_VERSION
76
77 #else
78 # define INSPIRCD_OPENSSL_OPAQUE_BIO
79 #endif
80
81 enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
82
83 static bool SelfSigned = false;
84 static int exdataindex;
85
86 char* get_error()
87 {
88         return ERR_error_string(ERR_get_error(), NULL);
89 }
90
91 static int OnVerify(int preverify_ok, X509_STORE_CTX* ctx);
92 static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
93
94 namespace OpenSSL
95 {
96         class Exception : public ModuleException
97         {
98          public:
99                 Exception(const std::string& reason)
100                         : ModuleException(reason) { }
101         };
102
103         class DHParams
104         {
105                 DH* dh;
106
107          public:
108                 DHParams(const std::string& filename)
109                 {
110                         BIO* dhpfile = BIO_new_file(filename.c_str(), "r");
111                         if (dhpfile == NULL)
112                                 throw Exception("Couldn't open DH file " + filename);
113
114                         dh = PEM_read_bio_DHparams(dhpfile, NULL, NULL, NULL);
115                         BIO_free(dhpfile);
116
117                         if (!dh)
118                                 throw Exception("Couldn't read DH params from file " + filename);
119                 }
120
121                 ~DHParams()
122                 {
123                         DH_free(dh);
124                 }
125
126                 DH* get()
127                 {
128                         return dh;
129                 }
130         };
131
132         class Context
133         {
134                 SSL_CTX* const ctx;
135                 long ctx_options;
136
137          public:
138                 Context(SSL_CTX* context)
139                         : ctx(context)
140                 {
141                         // Sane default options for OpenSSL see https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
142                         // and when choosing a cipher, use the server's preferences instead of the client preferences.
143                         long opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_DH_USE;
144                         // Only turn options on if they exist
145 #ifdef SSL_OP_SINGLE_ECDH_USE
146                         opts |= SSL_OP_SINGLE_ECDH_USE;
147 #endif
148 #ifdef SSL_OP_NO_TICKET
149                         opts |= SSL_OP_NO_TICKET;
150 #endif
151
152                         ctx_options = SSL_CTX_set_options(ctx, opts);
153
154                         long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
155 #ifdef SSL_MODE_RELEASE_BUFFERS
156                         mode |= SSL_MODE_RELEASE_BUFFERS;
157 #endif
158                         SSL_CTX_set_mode(ctx, mode);
159                         SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
160                         SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
161                         SSL_CTX_set_info_callback(ctx, StaticSSLInfoCallback);
162                 }
163
164                 ~Context()
165                 {
166                         SSL_CTX_free(ctx);
167                 }
168
169                 bool SetDH(DHParams& dh)
170                 {
171                         ERR_clear_error();
172                         return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
173                 }
174
175 #ifndef OPENSSL_NO_ECDH
176                 void SetECDH(const std::string& curvename)
177                 {
178                         int nid = OBJ_sn2nid(curvename.c_str());
179                         if (nid == 0)
180                                 throw Exception("Unknown curve: " + curvename);
181
182                         EC_KEY* eckey = EC_KEY_new_by_curve_name(nid);
183                         if (!eckey)
184                                 throw Exception("Unable to create EC key object");
185
186                         ERR_clear_error();
187                         bool ret = (SSL_CTX_set_tmp_ecdh(ctx, eckey) >= 0);
188                         EC_KEY_free(eckey);
189                         if (!ret)
190                                 throw Exception("Couldn't set ECDH parameters");
191                 }
192 #endif
193
194                 bool SetCiphers(const std::string& ciphers)
195                 {
196                         ERR_clear_error();
197                         return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
198                 }
199
200                 bool SetCerts(const std::string& filename)
201                 {
202                         ERR_clear_error();
203                         return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
204                 }
205
206                 bool SetPrivateKey(const std::string& filename)
207                 {
208                         ERR_clear_error();
209                         return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
210                 }
211
212                 bool SetCA(const std::string& filename)
213                 {
214                         ERR_clear_error();
215                         return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
216                 }
217
218                 long GetDefaultContextOptions() const
219                 {
220                         return ctx_options;
221                 }
222
223                 long SetRawContextOptions(long setoptions, long clearoptions)
224                 {
225                         // Clear everything
226                         SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
227
228                         // Set the default options and what is in the conf
229                         SSL_CTX_set_options(ctx, ctx_options | setoptions);
230                         return SSL_CTX_clear_options(ctx, clearoptions);
231                 }
232
233                 void SetVerifyCert()
234                 {
235                         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
236                 }
237
238                 SSL* CreateServerSession()
239                 {
240                         SSL* sess = SSL_new(ctx);
241                         SSL_set_accept_state(sess); // Act as server
242                         return sess;
243                 }
244
245                 SSL* CreateClientSession()
246                 {
247                         SSL* sess = SSL_new(ctx);
248                         SSL_set_connect_state(sess); // Act as client
249                         return sess;
250                 }
251         };
252
253         class Profile
254         {
255                 /** Name of this profile
256                  */
257                 const std::string name;
258
259                 /** DH parameters in use
260                  */
261                 DHParams dh;
262
263                 /** OpenSSL makes us have two contexts, one for servers and one for clients
264                  */
265                 Context ctx;
266                 Context clictx;
267
268                 /** Digest to use when generating fingerprints
269                  */
270                 const EVP_MD* digest;
271
272                 /** Last error, set by error_callback()
273                  */
274                 std::string lasterr;
275
276                 /** True if renegotiations are allowed, false if not
277                  */
278                 const bool allowrenego;
279
280                 /** Rough max size of records to send
281                  */
282                 const unsigned int outrecsize;
283
284                 static int error_callback(const char* str, size_t len, void* u)
285                 {
286                         Profile* profile = reinterpret_cast<Profile*>(u);
287                         profile->lasterr = std::string(str, len - 1);
288                         return 0;
289                 }
290
291                 /** Set raw OpenSSL context (SSL_CTX) options from a config tag
292                  * @param ctxname Name of the context, client or server
293                  * @param tag Config tag defining this profile
294                  * @param context Context object to manipulate
295                  */
296                 void SetContextOptions(const std::string& ctxname, ConfigTag* tag, Context& context)
297                 {
298                         long setoptions = tag->getInt(ctxname + "setoptions");
299                         long clearoptions = tag->getInt(ctxname + "clearoptions");
300 #ifdef SSL_OP_NO_COMPRESSION
301                         if (!tag->getBool("compression", false)) // Disable compression by default
302                                 setoptions |= SSL_OP_NO_COMPRESSION;
303 #endif
304                         // Disable TLSv1.0 by default.
305                         if (!tag->getBool("tlsv1", false))
306                                 setoptions |= SSL_OP_NO_TLSv1;
307
308                         if (!setoptions && !clearoptions)
309                                 return; // Nothing to do
310
311                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Setting %s %s context options, default: %ld set: %ld clear: %ld", name.c_str(), ctxname.c_str(), ctx.GetDefaultContextOptions(), setoptions, clearoptions);
312                         long final = context.SetRawContextOptions(setoptions, clearoptions);
313                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "%s %s context options: %ld", name.c_str(), ctxname.c_str(), final);
314                 }
315
316          public:
317                 Profile(const std::string& profilename, ConfigTag* tag)
318                         : name(profilename)
319                         , dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "dhparams.pem")))
320                         , ctx(SSL_CTX_new(SSLv23_server_method()))
321                         , clictx(SSL_CTX_new(SSLv23_client_method()))
322                         , allowrenego(tag->getBool("renegotiation")) // Disallow by default
323                         , outrecsize(tag->getInt("outrecsize", 2048, 512, 16384))
324                 {
325                         if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
326                                 throw Exception("Couldn't set DH parameters");
327
328                         std::string hash = tag->getString("hash", "md5");
329                         digest = EVP_get_digestbyname(hash.c_str());
330                         if (digest == NULL)
331                                 throw Exception("Unknown hash type " + hash);
332
333                         std::string ciphers = tag->getString("ciphers");
334                         if (!ciphers.empty())
335                         {
336                                 if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers)))
337                                 {
338                                         ERR_print_errors_cb(error_callback, this);
339                                         throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr);
340                                 }
341                         }
342
343 #ifndef OPENSSL_NO_ECDH
344                         std::string curvename = tag->getString("ecdhcurve", "prime256v1");
345                         if (!curvename.empty())
346                                 ctx.SetECDH(curvename);
347 #endif
348
349                         SetContextOptions("server", tag, ctx);
350                         SetContextOptions("client", tag, clictx);
351
352                         /* Load our keys and certificates
353                          * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
354                          */
355                         std::string filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("certfile", "cert.pem"));
356                         if ((!ctx.SetCerts(filename)) || (!clictx.SetCerts(filename)))
357                         {
358                                 ERR_print_errors_cb(error_callback, this);
359                                 throw Exception("Can't read certificate file: " + lasterr);
360                         }
361
362                         filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("keyfile", "key.pem"));
363                         if ((!ctx.SetPrivateKey(filename)) || (!clictx.SetPrivateKey(filename)))
364                         {
365                                 ERR_print_errors_cb(error_callback, this);
366                                 throw Exception("Can't read key file: " + lasterr);
367                         }
368
369                         // Load the CAs we trust
370                         filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("cafile", "ca.pem"));
371                         if ((!ctx.SetCA(filename)) || (!clictx.SetCA(filename)))
372                         {
373                                 ERR_print_errors_cb(error_callback, this);
374                                 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", filename.c_str(), lasterr.c_str());
375                         }
376
377                         clictx.SetVerifyCert();
378                         if (tag->getBool("requestclientcert", true))
379                                 ctx.SetVerifyCert();
380                 }
381
382                 const std::string& GetName() const { return name; }
383                 SSL* CreateServerSession() { return ctx.CreateServerSession(); }
384                 SSL* CreateClientSession() { return clictx.CreateClientSession(); }
385                 const EVP_MD* GetDigest() { return digest; }
386                 bool AllowRenegotiation() const { return allowrenego; }
387                 unsigned int GetOutgoingRecordSize() const { return outrecsize; }
388         };
389
390         namespace BIOMethod
391         {
392                 static int create(BIO* bio)
393                 {
394                         BIO_set_init(bio, 1);
395                         return 1;
396                 }
397
398                 static int destroy(BIO* bio)
399                 {
400                         // XXX: Dummy function to avoid a memory leak in OpenSSL.
401                         // The memory leak happens in BIO_free() (bio_lib.c) when the destroy func of the BIO is NULL.
402                         // This is fixed in OpenSSL but some distros still ship the unpatched version hence we provide this workaround.
403                         return 1;
404                 }
405
406                 static long ctrl(BIO* bio, int cmd, long num, void* ptr)
407                 {
408                         if (cmd == BIO_CTRL_FLUSH)
409                                 return 1;
410                         return 0;
411                 }
412
413                 static int read(BIO* bio, char* buf, int len);
414                 static int write(BIO* bio, const char* buf, int len);
415
416 #ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
417                 static BIO_METHOD* alloc()
418                 {
419                         BIO_METHOD* meth = BIO_meth_new(100 | BIO_TYPE_SOURCE_SINK, "inspircd");
420                         BIO_meth_set_write(meth, OpenSSL::BIOMethod::write);
421                         BIO_meth_set_read(meth, OpenSSL::BIOMethod::read);
422                         BIO_meth_set_ctrl(meth, OpenSSL::BIOMethod::ctrl);
423                         BIO_meth_set_create(meth, OpenSSL::BIOMethod::create);
424                         BIO_meth_set_destroy(meth, OpenSSL::BIOMethod::destroy);
425                         return meth;
426                 }
427 #endif
428         }
429 }
430
431 // BIO_METHOD is opaque in OpenSSL 1.1 so we can't do this.
432 // See OpenSSL::BIOMethod::alloc for the new method.
433 #ifndef INSPIRCD_OPENSSL_OPAQUE_BIO
434 static BIO_METHOD biomethods =
435 {
436         (100 | BIO_TYPE_SOURCE_SINK),
437         "inspircd",
438         OpenSSL::BIOMethod::write,
439         OpenSSL::BIOMethod::read,
440         NULL, // puts
441         NULL, // gets
442         OpenSSL::BIOMethod::ctrl,
443         OpenSSL::BIOMethod::create,
444         OpenSSL::BIOMethod::destroy, // destroy, does nothing, see function body for more info
445         NULL // callback_ctrl
446 };
447 #else
448 static BIO_METHOD* biomethods;
449 #endif
450
451 static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx)
452 {
453         /* XXX: This will allow self signed certificates.
454          * In the future if we want an option to not allow this,
455          * we can just return preverify_ok here, and openssl
456          * will boot off self-signed and invalid peer certs.
457          */
458         int ve = X509_STORE_CTX_get_error(ctx);
459
460         SelfSigned = (ve == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
461
462         return 1;
463 }
464
465 class OpenSSLIOHook : public SSLIOHook
466 {
467  private:
468         SSL* sess;
469         issl_status status;
470         bool data_to_write;
471
472         // Returns 1 if handshake succeeded, 0 if it is still in progress, -1 if it failed
473         int Handshake(StreamSocket* user)
474         {
475                 ERR_clear_error();
476                 int ret = SSL_do_handshake(sess);
477                 if (ret < 0)
478                 {
479                         int err = SSL_get_error(sess, ret);
480
481                         if (err == SSL_ERROR_WANT_READ)
482                         {
483                                 SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
484                                 this->status = ISSL_HANDSHAKING;
485                                 return 0;
486                         }
487                         else if (err == SSL_ERROR_WANT_WRITE)
488                         {
489                                 SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
490                                 this->status = ISSL_HANDSHAKING;
491                                 return 0;
492                         }
493                         else
494                         {
495                                 CloseSession();
496                                 return -1;
497                         }
498                 }
499                 else if (ret > 0)
500                 {
501                         // Handshake complete.
502                         VerifyCertificate();
503
504                         status = ISSL_OPEN;
505
506                         SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE);
507
508                         return 1;
509                 }
510                 else if (ret == 0)
511                 {
512                         CloseSession();
513                 }
514                 return -1;
515         }
516
517         void CloseSession()
518         {
519                 if (sess)
520                 {
521                         SSL_shutdown(sess);
522                         SSL_free(sess);
523                 }
524                 sess = NULL;
525                 certificate = NULL;
526                 status = ISSL_NONE;
527         }
528
529         void VerifyCertificate()
530         {
531                 X509* cert;
532                 ssl_cert* certinfo = new ssl_cert;
533                 this->certificate = certinfo;
534                 unsigned int n;
535                 unsigned char md[EVP_MAX_MD_SIZE];
536
537                 cert = SSL_get_peer_certificate(sess);
538
539                 if (!cert)
540                 {
541                         certinfo->error = "Could not get peer certificate: "+std::string(get_error());
542                         return;
543                 }
544
545                 certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK);
546
547                 if (!SelfSigned)
548                 {
549                         certinfo->unknownsigner = false;
550                         certinfo->trusted = true;
551                 }
552                 else
553                 {
554                         certinfo->unknownsigner = true;
555                         certinfo->trusted = false;
556                 }
557
558                 char buf[512];
559                 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
560                 certinfo->dn = buf;
561                 // Make sure there are no chars in the string that we consider invalid
562                 if (certinfo->dn.find_first_of("\r\n") != std::string::npos)
563                         certinfo->dn.clear();
564
565                 X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
566                 certinfo->issuer = buf;
567                 if (certinfo->issuer.find_first_of("\r\n") != std::string::npos)
568                         certinfo->issuer.clear();
569
570                 if (!X509_digest(cert, GetProfile().GetDigest(), md, &n))
571                 {
572                         certinfo->error = "Out of memory generating fingerprint";
573                 }
574                 else
575                 {
576                         certinfo->fingerprint = BinToHex(md, n);
577                 }
578
579                 if ((ASN1_UTCTIME_cmp_time_t(X509_getm_notAfter(cert), ServerInstance->Time()) == -1) || (ASN1_UTCTIME_cmp_time_t(X509_getm_notBefore(cert), ServerInstance->Time()) == 0))
580                 {
581                         certinfo->error = "Not activated, or expired certificate";
582                 }
583
584                 X509_free(cert);
585         }
586
587         void SSLInfoCallback(int where, int rc)
588         {
589                 if ((where & SSL_CB_HANDSHAKE_START) && (status == ISSL_OPEN))
590                 {
591                         if (GetProfile().AllowRenegotiation())
592                                 return;
593
594                         // The other side is trying to renegotiate, kill the connection and change status
595                         // to ISSL_NONE so CheckRenego() closes the session
596                         status = ISSL_NONE;
597                         BIO* bio = SSL_get_rbio(sess);
598                         EventHandler* eh = static_cast<StreamSocket*>(BIO_get_data(bio));
599                         SocketEngine::Shutdown(eh, 2);
600                 }
601         }
602
603         bool CheckRenego(StreamSocket* sock)
604         {
605                 if (status != ISSL_NONE)
606                         return true;
607
608                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Session %p killed, attempted to renegotiate", (void*)sess);
609                 CloseSession();
610                 sock->SetError("Renegotiation is not allowed");
611                 return false;
612         }
613
614         // Returns 1 if application I/O should proceed, 0 if it must wait for the underlying protocol to progress, -1 on fatal error
615         int PrepareIO(StreamSocket* sock)
616         {
617                 if (status == ISSL_OPEN)
618                         return 1;
619                 else if (status == ISSL_HANDSHAKING)
620                 {
621                         // The handshake isn't finished, try to finish it
622                         return Handshake(sock);
623                 }
624
625                 CloseSession();
626                 return -1;
627         }
628
629         // Calls our private SSLInfoCallback()
630         friend void StaticSSLInfoCallback(const SSL* ssl, int where, int rc);
631
632  public:
633         OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, SSL* session)
634                 : SSLIOHook(hookprov)
635                 , sess(session)
636                 , status(ISSL_NONE)
637                 , data_to_write(false)
638         {
639                 // Create BIO instance and store a pointer to the socket in it which will be used by the read and write functions
640 #ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
641                 BIO* bio = BIO_new(biomethods);
642 #else
643                 BIO* bio = BIO_new(&biomethods);
644 #endif
645                 BIO_set_data(bio, sock);
646                 SSL_set_bio(sess, bio, bio);
647
648                 SSL_set_ex_data(sess, exdataindex, this);
649                 sock->AddIOHook(this);
650                 Handshake(sock);
651         }
652
653         void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE
654         {
655                 CloseSession();
656         }
657
658         int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE
659         {
660                 // Finish handshake if needed
661                 int prepret = PrepareIO(user);
662                 if (prepret <= 0)
663                         return prepret;
664
665                 // If we resumed the handshake then this->status will be ISSL_OPEN
666                 {
667                         ERR_clear_error();
668                         char* buffer = ServerInstance->GetReadBuffer();
669                         size_t bufsiz = ServerInstance->Config->NetBufferSize;
670                         int ret = SSL_read(sess, buffer, bufsiz);
671
672                         if (!CheckRenego(user))
673                                 return -1;
674
675                         if (ret > 0)
676                         {
677                                 recvq.append(buffer, ret);
678                                 int mask = 0;
679                                 // Schedule a read if there is still data in the OpenSSL buffer
680                                 if (SSL_pending(sess) > 0)
681                                         mask |= FD_ADD_TRIAL_READ;
682                                 if (data_to_write)
683                                         mask |= FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE;
684                                 if (mask != 0)
685                                         SocketEngine::ChangeEventMask(user, mask);
686                                 return 1;
687                         }
688                         else if (ret == 0)
689                         {
690                                 // Client closed connection.
691                                 CloseSession();
692                                 user->SetError("Connection closed");
693                                 return -1;
694                         }
695                         else // if (ret < 0)
696                         {
697                                 int err = SSL_get_error(sess, ret);
698
699                                 if (err == SSL_ERROR_WANT_READ)
700                                 {
701                                         SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
702                                         return 0;
703                                 }
704                                 else if (err == SSL_ERROR_WANT_WRITE)
705                                 {
706                                         SocketEngine::ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE);
707                                         return 0;
708                                 }
709                                 else
710                                 {
711                                         CloseSession();
712                                         return -1;
713                                 }
714                         }
715                 }
716         }
717
718         int OnStreamSocketWrite(StreamSocket* user, StreamSocket::SendQueue& sendq) CXX11_OVERRIDE
719         {
720                 // Finish handshake if needed
721                 int prepret = PrepareIO(user);
722                 if (prepret <= 0)
723                         return prepret;
724
725                 data_to_write = true;
726
727                 // Session is ready for transferring application data
728                 while (!sendq.empty())
729                 {
730                         ERR_clear_error();
731                         FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize());
732                         const StreamSocket::SendQueue::Element& buffer = sendq.front();
733                         int ret = SSL_write(sess, buffer.data(), buffer.size());
734
735                         if (!CheckRenego(user))
736                                 return -1;
737
738                         if (ret == (int)buffer.length())
739                         {
740                                 // Wrote entire record, continue sending
741                                 sendq.pop_front();
742                         }
743                         else if (ret > 0)
744                         {
745                                 sendq.erase_front(ret);
746                                 SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
747                                 return 0;
748                         }
749                         else if (ret == 0)
750                         {
751                                 CloseSession();
752                                 return -1;
753                         }
754                         else // if (ret < 0)
755                         {
756                                 int err = SSL_get_error(sess, ret);
757
758                                 if (err == SSL_ERROR_WANT_WRITE)
759                                 {
760                                         SocketEngine::ChangeEventMask(user, FD_WANT_SINGLE_WRITE);
761                                         return 0;
762                                 }
763                                 else if (err == SSL_ERROR_WANT_READ)
764                                 {
765                                         SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ);
766                                         return 0;
767                                 }
768                                 else
769                                 {
770                                         CloseSession();
771                                         return -1;
772                                 }
773                         }
774                 }
775
776                 data_to_write = false;
777                 SocketEngine::ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
778                 return 1;
779         }
780
781         void GetCiphersuite(std::string& out) const CXX11_OVERRIDE
782         {
783                 if (!IsHandshakeDone())
784                         return;
785                 out.append(SSL_get_version(sess)).push_back('-');
786                 out.append(SSL_get_cipher(sess));
787         }
788
789         bool GetServerName(std::string& out) const CXX11_OVERRIDE
790         {
791                 const char* name = SSL_get_servername(sess, TLSEXT_NAMETYPE_host_name);
792                 if (!name)
793                         return false;
794
795                 out.append(name);
796                 return true;
797         }
798
799         bool IsHandshakeDone() const { return (status == ISSL_OPEN); }
800         OpenSSL::Profile& GetProfile();
801 };
802
803 static void StaticSSLInfoCallback(const SSL* ssl, int where, int rc)
804 {
805         OpenSSLIOHook* hook = static_cast<OpenSSLIOHook*>(SSL_get_ex_data(ssl, exdataindex));
806         hook->SSLInfoCallback(where, rc);
807 }
808
809 static int OpenSSL::BIOMethod::write(BIO* bio, const char* buffer, int size)
810 {
811         BIO_clear_retry_flags(bio);
812
813         StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
814         if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK)
815         {
816                 // Writes blocked earlier, don't retry syscall
817                 BIO_set_retry_write(bio);
818                 return -1;
819         }
820
821         int ret = SocketEngine::Send(sock, buffer, size, 0);
822         if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
823         {
824                 // Blocked, set retry flag for OpenSSL
825                 SocketEngine::ChangeEventMask(sock, FD_WRITE_WILL_BLOCK);
826                 BIO_set_retry_write(bio);
827         }
828
829         return ret;
830 }
831
832 static int OpenSSL::BIOMethod::read(BIO* bio, char* buffer, int size)
833 {
834         BIO_clear_retry_flags(bio);
835
836         StreamSocket* sock = static_cast<StreamSocket*>(BIO_get_data(bio));
837         if (sock->GetEventMask() & FD_READ_WILL_BLOCK)
838         {
839                 // Reads blocked earlier, don't retry syscall
840                 BIO_set_retry_read(bio);
841                 return -1;
842         }
843
844         int ret = SocketEngine::Recv(sock, buffer, size, 0);
845         if ((ret < size) && ((ret > 0) || (SocketEngine::IgnoreError())))
846         {
847                 // Blocked, set retry flag for OpenSSL
848                 SocketEngine::ChangeEventMask(sock, FD_READ_WILL_BLOCK);
849                 BIO_set_retry_read(bio);
850         }
851
852         return ret;
853 }
854
855 class OpenSSLIOHookProvider : public IOHookProvider
856 {
857         OpenSSL::Profile profile;
858
859  public:
860         OpenSSLIOHookProvider(Module* mod, const std::string& profilename, ConfigTag* tag)
861                 : IOHookProvider(mod, "ssl/" + profilename, IOHookProvider::IOH_SSL)
862                 , profile(profilename, tag)
863         {
864                 ServerInstance->Modules->AddService(*this);
865         }
866
867         ~OpenSSLIOHookProvider()
868         {
869                 ServerInstance->Modules->DelService(*this);
870         }
871
872         void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
873         {
874                 new OpenSSLIOHook(this, sock, profile.CreateServerSession());
875         }
876
877         void OnConnect(StreamSocket* sock) CXX11_OVERRIDE
878         {
879                 new OpenSSLIOHook(this, sock, profile.CreateClientSession());
880         }
881
882         OpenSSL::Profile& GetProfile() { return profile; }
883 };
884
885 OpenSSL::Profile& OpenSSLIOHook::GetProfile()
886 {
887         IOHookProvider* hookprov = prov;
888         return static_cast<OpenSSLIOHookProvider*>(hookprov)->GetProfile();
889 }
890
891 class ModuleSSLOpenSSL : public Module
892 {
893         typedef std::vector<reference<OpenSSLIOHookProvider> > ProfileList;
894
895         ProfileList profiles;
896
897         void ReadProfiles()
898         {
899                 ProfileList newprofiles;
900                 ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile");
901                 if (tags.first == tags.second)
902                 {
903                         // Create a default profile named "openssl"
904                         const std::string defname = "openssl";
905                         ConfigTag* tag = ServerInstance->Config->ConfValue(defname);
906                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No <sslprofile> tags found, using settings from the <openssl> tag");
907
908                         try
909                         {
910                                 newprofiles.push_back(new OpenSSLIOHookProvider(this, defname, tag));
911                         }
912                         catch (OpenSSL::Exception& ex)
913                         {
914                                 throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason());
915                         }
916                 }
917
918                 for (ConfigIter i = tags.first; i != tags.second; ++i)
919                 {
920                         ConfigTag* tag = i->second;
921                         if (tag->getString("provider") != "openssl")
922                                 continue;
923
924                         std::string name = tag->getString("name");
925                         if (name.empty())
926                         {
927                                 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring <sslprofile> tag without name at " + tag->getTagLocation());
928                                 continue;
929                         }
930
931                         reference<OpenSSLIOHookProvider> prov;
932                         try
933                         {
934                                 prov = new OpenSSLIOHookProvider(this, name, tag);
935                         }
936                         catch (CoreException& ex)
937                         {
938                                 throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason());
939                         }
940
941                         newprofiles.push_back(prov);
942                 }
943
944                 for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i)
945                 {
946                         OpenSSLIOHookProvider& prov = **i;
947                         ServerInstance->Modules.DelService(prov);
948                 }
949
950                 profiles.swap(newprofiles);
951         }
952
953  public:
954         ModuleSSLOpenSSL()
955         {
956                 // Initialize OpenSSL
957                 OPENSSL_init_ssl(0, NULL);
958 #ifdef INSPIRCD_OPENSSL_OPAQUE_BIO
959                 biomethods = OpenSSL::BIOMethod::alloc();
960         }
961
962         ~ModuleSSLOpenSSL()
963         {
964                 BIO_meth_free(biomethods);
965 #endif
966         }
967
968         void init() CXX11_OVERRIDE
969         {
970                 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "OpenSSL lib version \"%s\" module was compiled for \"" OPENSSL_VERSION_TEXT "\"", OpenSSL_version(OPENSSL_VERSION));
971
972                 // Register application specific data
973                 char exdatastr[] = "inspircd";
974                 exdataindex = SSL_get_ex_new_index(0, exdatastr, NULL, NULL, NULL);
975                 if (exdataindex < 0)
976                         throw ModuleException("Failed to register application specific data");
977
978                 ReadProfiles();
979         }
980
981         void OnModuleRehash(User* user, const std::string &param) CXX11_OVERRIDE
982         {
983                 if (param != "ssl")
984                         return;
985
986                 try
987                 {
988                         ReadProfiles();
989                 }
990                 catch (ModuleException& ex)
991                 {
992                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, ex.GetReason() + " Not applying settings.");
993                 }
994         }
995
996         void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
997         {
998                 if (type == ExtensionItem::EXT_USER)
999                 {
1000                         LocalUser* user = IS_LOCAL((User*)item);
1001
1002                         if ((user) && (user->eh.GetModHook(this)))
1003                         {
1004                                 // User is using SSL, they're a local user, and they're using one of *our* SSL ports.
1005                                 // Potentially there could be multiple SSL modules loaded at once on different ports.
1006                                 ServerInstance->Users->QuitUser(user, "SSL module unloading");
1007                         }
1008                 }
1009         }
1010
1011         ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE
1012         {
1013                 const OpenSSLIOHook* const iohook = static_cast<OpenSSLIOHook*>(user->eh.GetModHook(this));
1014                 if ((iohook) && (!iohook->IsHandshakeDone()))
1015                         return MOD_RES_DENY;
1016                 return MOD_RES_PASSTHRU;
1017         }
1018
1019         Version GetVersion() CXX11_OVERRIDE
1020         {
1021                 return Version("Provides SSL support for clients", VF_VENDOR);
1022         }
1023 };
1024
1025 MODULE_INIT(ModuleSSLOpenSSL)