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