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